shazam/0000755000176200001440000000000014506610333011536 5ustar liggesusersshazam/NAMESPACE0000644000176200001440000001252614502023570012760 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(calcBaseline) export(calcExpectedMutations) export(calcObservedMutations) export(calcTargetingDistance) export(calculateMutability) export(collapseClones) export(consensusSequence) export(convertNumbering) export(createBaseline) export(createMutabilityMatrix) export(createMutationDefinition) export(createRegionDefinition) export(createSubstitutionMatrix) export(createTargetingMatrix) export(createTargetingModel) export(distToNearest) export(editBaseline) export(expectedMutations) export(extendMutabilityMatrix) export(extendSubstitutionMatrix) export(findThreshold) export(groupBaseline) export(makeAverage1merMut) export(makeAverage1merSub) export(makeDegenerate5merMut) export(makeDegenerate5merSub) export(makeGraphDf) export(minNumMutationsTune) export(minNumSeqMutationsTune) export(observedMutations) export(plotBaselineDensity) export(plotBaselineSummary) export(plotDensityThreshold) export(plotGmmThreshold) export(plotMutability) export(plotSlideWindowTune) export(plotTune) export(setRegionBoundaries) export(shmulateSeq) export(shmulateTree) export(slideWindowDb) export(slideWindowSeq) export(slideWindowTune) export(slideWindowTunePlot) export(summarizeBaseline) export(testBaseline) export(writeTargetingDistance) exportClasses(Baseline) exportClasses(DensityThreshold) exportClasses(GmmThreshold) exportClasses(MutabilityModel) exportClasses(MutationDefinition) exportClasses(RegionDefinition) exportClasses(TargetingMatrix) exportClasses(TargetingModel) exportMethods(as.data.frame) exportMethods(plot) exportMethods(print) exportMethods(summary) import(ggplot2) import(graphics) import(methods) import(utils) importFrom(KernSmooth,bkde) importFrom(MASS,fitdistr) importFrom(alakazam,IUPAC_DNA) importFrom(alakazam,baseTheme) importFrom(alakazam,buildPhylipLineage) importFrom(alakazam,checkColumns) importFrom(alakazam,cpuCount) importFrom(alakazam,getAAMatrix) importFrom(alakazam,getAllele) importFrom(alakazam,getDNAMatrix) importFrom(alakazam,getFamily) importFrom(alakazam,getGene) importFrom(alakazam,getLocus) importFrom(alakazam,getMRCA) importFrom(alakazam,getPathLengths) importFrom(alakazam,getSegment) importFrom(alakazam,gridPlot) importFrom(alakazam,groupGenes) importFrom(alakazam,isValidAASeq) importFrom(alakazam,makeChangeoClone) importFrom(alakazam,nonsquareDist) importFrom(alakazam,pairwiseDist) importFrom(alakazam,pairwiseEqual) importFrom(alakazam,progressBar) importFrom(alakazam,seqDist) importFrom(alakazam,seqEqual) importFrom(alakazam,summarizeSubtrees) importFrom(alakazam,tableEdges) importFrom(alakazam,translateStrings) importFrom(ape,mst) importFrom(diptest,dip.test) importFrom(doParallel,registerDoParallel) importFrom(dplyr,"%>%") importFrom(dplyr,arrange) importFrom(dplyr,bind_cols) importFrom(dplyr,bind_rows) importFrom(dplyr,combine) importFrom(dplyr,desc) importFrom(dplyr,do) importFrom(dplyr,filter) importFrom(dplyr,group_by) importFrom(dplyr,group_indices) importFrom(dplyr,left_join) importFrom(dplyr,mutate) importFrom(dplyr,mutate_at) importFrom(dplyr,n) importFrom(dplyr,pull) importFrom(dplyr,recode) importFrom(dplyr,rename) importFrom(dplyr,select) importFrom(dplyr,summarize) importFrom(dplyr,summarize_at) importFrom(dplyr,transmute) importFrom(dplyr,ungroup) importFrom(foreach,"%dopar%") importFrom(foreach,foreach) importFrom(foreach,registerDoSEQ) importFrom(igraph,"V<-") importFrom(igraph,E) importFrom(igraph,V) importFrom(igraph,as_adjacency_matrix) importFrom(igraph,graph_from_data_frame) importFrom(igraph,layout_as_tree) importFrom(igraph,set_vertex_attr) importFrom(igraph,vertex_attr) importFrom(iterators,icount) importFrom(lazyeval,interp) importFrom(progress,progress_bar) importFrom(rlang,.data) importFrom(rlang,sym) importFrom(rlang,syms) importFrom(scales,log10_trans) importFrom(scales,log2_trans) importFrom(scales,math_format) importFrom(scales,percent) importFrom(scales,pretty_breaks) importFrom(scales,scientific) importFrom(scales,trans_breaks) importFrom(scales,trans_format) importFrom(seqinr,c2s) importFrom(seqinr,s2c) importFrom(seqinr,translate) importFrom(seqinr,words) importFrom(stats,approx) importFrom(stats,as.dist) importFrom(stats,convolve) importFrom(stats,cor) importFrom(stats,cov) importFrom(stats,cutree) importFrom(stats,dbeta) importFrom(stats,dgamma) importFrom(stats,dnorm) importFrom(stats,ecdf) importFrom(stats,integrate) importFrom(stats,mad) importFrom(stats,median) importFrom(stats,na.exclude) importFrom(stats,na.omit) importFrom(stats,optim) importFrom(stats,optimize) importFrom(stats,p.adjust) importFrom(stats,pbeta) importFrom(stats,pgamma) importFrom(stats,pnorm) importFrom(stats,qbeta) importFrom(stats,rbeta) importFrom(stats,runif) importFrom(stats,sd) importFrom(stats,setNames) importFrom(stats,uniroot) importFrom(stats,weighted.mean) importFrom(stringi,stri_count_boundaries) importFrom(stringi,stri_count_regex) importFrom(stringi,stri_detect_regex) importFrom(stringi,stri_dup) importFrom(stringi,stri_extract_all_regex) importFrom(stringi,stri_extract_first_regex) importFrom(stringi,stri_flatten) importFrom(stringi,stri_join) importFrom(stringi,stri_length) importFrom(stringi,stri_replace_all_regex) importFrom(stringi,stri_replace_first_regex) importFrom(stringi,stri_sub) importFrom(stringi,stri_sub_replace) importFrom(tidyr,gather) importFrom(tidyr,pivot_wider) importFrom(tidyr,spread) importFrom(tidyselect,all_of) importFrom(tidyselect,any_of) shazam/README.md0000644000176200001440000000513214471627005013023 0ustar liggesusers[![](http://cranlogs.r-pkg.org/badges/grand-total/shazam)](https://www.r-pkg.org/pkg/shazam) [![](https://cranlogs.r-pkg.org/badges/shazam)](https://www.r-pkg.org/pkg/shazam) [![](https://img.shields.io/static/v1?label=AIRR-C%20sw-tools%20v1&message=compliant&color=008AFF&labelColor=000000&style=plastic)](https://docs.airr-community.org/en/stable/swtools/airr_swtools_standard.html) SHazaM ------------------------------------------------------------------------------- SHazaM is part of the [Immcantation](http://immcantation.readthedocs.io) analysis framework for Adaptive Immune Receptor Repertoire sequencing (AIRR-seq) and provides tools for advanced analysis of somatic hypermutation (SHM) in immunoglobulin (Ig) sequences. Shazam focuses on the following analysis topics: 1. **Quantification of mutational load** SHazaM includes methods for determine the rate of observed and expected mutations under various criteria. Mutational profiling criteria include rates under SHM targeting models, mutations specific to CDR and FWR regions, and physicochemical property dependent substitution rates. 2. **Statistical models of SHM targeting patterns** Models of SHM may be divided into two independent components: (a) a mutability model that defines where mutations occur and (b) a nucleotide substitution model that defines the resulting mutation. Collectively these two components define an SHM targeting model. SHazaM provides empirically derived SHM 5-mer context mutation models for both humans and mice, as well tools to build SHM targeting models from data. 3. **Analysis of selection pressure using BASELINe** The Bayesian Estimation of Antigen-driven Selection in Ig Sequences (BASELINe) method is a novel method for quantifying antigen-driven selection in high-throughput Ig sequence data. BASELINe uses SHM targeting models can be used to estimate the null distribution of expected mutation frequencies, and provide measures of selection pressure informed by known AID targeting biases. 4. **Model-dependent distance calculations** SHazaM provides methods to compute evolutionary distances between sequences or set of sequences based on SHM targeting models. This information is particularly useful in understanding and defining clonal relationships. Contact ------------------------------------------------------------------------------- For help and questions please contact the [Immcantation Group](mailto:immcantation@googlegroups.com) or use the [issue tracker](https://bitbucket.org/kleinstein/shazam/issues?status=new&status=open). shazam/data/0000755000176200001440000000000014027340034012443 5ustar liggesusersshazam/data/MK_RS5NF.rda0000644000176200001440000047261613670240241014401 0ustar liggesusersPM7|1HhbZ"9*9Y1g1ĝbF1"*(`Y_ '佫ꬺ{j={lgwfӯWRJU+UQgTt4TZFJ**{m(*U꯿/$.k['<"9,)&!%&_df\DrJHRTDJL(ʡ!"EEKQVRSW[M9Jϭ&+S7&*:E3,:lM~W99"DdȤ8e׿_̐׊ m+%'DD$O.{}']𐔿R[OGPSGWS~/*16v1"&)^KC٧샗1"&jrtJJvx|V|RvlkgWqk|1}cRWT~fHlB: o#Wkd޷iǫw0 ;Ov6^R泈ϟUم%\~R쇝{qרY--'H2.J\םlz\,Cǭ2\I"^mIƓΑBo'UureZٗvPElT{M{J!QI'$Ls(A[FPPqfPx/wϑdz;fwj52ZSzkDQOjP'SUJ)TMOqFY5bRpe᝖R0SKFxQTOG!#Z r/^5Tzg@ /Qɞ,x@ uWߟJ:*9ۢPjWPXJ6qS ;Uk{'§'9F66yL\q&_ek'b߳7O|:vpJܞ^z LOyo7uqPyG8ٮ;6Y'TysjnrSpYNU7=YyuZ4XsCt*Q~V#ȩ*cg{]mZ[D7G pfls"2:Yt2+]@Z;F)]hGO}KZX O^VO(n]^t z4:ɤkYŰTW2H3-Lk-_0̚,<2 ;rEP~0Y_zȬq" IE%F^řȲcfdrԺժ~d7pY:Y s/FҪ_&>7HjqdMC/Lu2o{6k^W'Ul_mXxDNΤ0hɤksoۊtHrRt_acV8R2/^Kҽtb nmՏG]l֠7Yw|#rߡs &^LfECS]N{q]Wddyלj+Wk*W!־Q!>pY7"gr@6/:@^_ܱFXes)ܕ;h.u?:PsDԭܲD{ȸ;{9~z^Sދ_&.N6O]IRf=,#F=z:t5C՘8W~Lڗ_h_G}Ú$$q[ҽdkڮl^^KԱRN2lZz__L)\AZ9s# H?~EzIXaCs!]PGIo!Y }IStF$%xf":Ɂd>8IMɹӪߓLj^HRΗ~͍.CkvPwC즐6v/52ݕ3#R(dPW} Ipmލ–ӅA׆Pȼ꧌vݣXӟ3r{eJ C{kI ٗhv4hu:hibuΝV4;hxuO-m:i#w4>.n7ELT[:Ы Eo5QUꌁVnAv~{i>Jprv >spjvCXjL{9B]r,0;& ֵTMD]lxsNW-fv4`lϏm(~>?O` aaĘ{zW /#g=uS:mZr:MUuu*yhE#:(YEq8m]m:ve~Wdbb糤0|z)3wu^}ֆ}2?]IdsuA\p4}vK׿V&<6²Kvk2xwz"c 6!=khAV/"[\* g*9 ږDXVomFu^q6NRmN5>w֙<5uDӷn*ƽ|vEg{w4IR=6yoE&yMZ>tLiɕU}ݫͮsZ{m&IM5yCgF^LJN~fsvGz_pf',]0qH.y /rlwxz diۀK-Bߏ|W9cu:} >U{@b*=eKߓ7n?'oN}rkUa듖5OaճPHNLԛfخef !Mfe&ԧgӃ-etj䷰;Eo4g'~a}{]fVF[˖#{ #\GÁ2U8'fYXwf}y]nk[|0IsDlb̯֙:ms2nIjkFYj\2}l, h]ϦNZu=M*BfΖw"[QV> ֐aȗ \t-dRu}e_# 5dy2s9UӲ?]Xɞś_րmh2Pm0h}*՟!E3nxd0oJdq{ie;F<52XcssWnf ڕ_tx-GNxPhOlWlНR=/!wVޫ-Z{!z\78ݬs][r63,|rm|R.eAS iˆb(:|jse].AD>F5-[tr /߈|<[޻`3イ:v |G,{J6w/MJ!~cnPyPȶ˭\܏>V:zz]9h rw\ea.4pfv!YlJ?rX]#aqÎW> ǒӻGϦiRnak]m%7| oԩ 'H'!g]c(P Qʔ6b8k ^R9#(*+!_oD'NҿJfc[WzԊ=A~^̇ǭ͢v%#yV&^mI]EɁd~vV+RO*y{>=Ia H)mAOH+.&3'VPq+js搕 eidhUH~bҋҞӺfO2i.G:'ȉ, 2Xho(2krΈxpwts[mSηJg5m#:xj .1.J F._6Y:jn-L8mj)9ZՔ'y]~w|{#f$ 9֑ӏuF+QFK=Uɯ_ΧR쪺|aO=srsG &}TDZћ*guȾ{g*5ީ'e<#{NBo?0?oۚDMuPАf"KO7N/$D/k(VU1tkחk.Dfz'_}ժ4qɂ<4J8Ay+l4EwΞT_n.QDN|oYg4ӅN2G+o\b,rledQBia?QgˎnݤwV9/ANcUbf_D\8C,cdik^4ou~\+QHW:RfM#k)_`ne8wr/M 0:^{P}>{G T_;oLnF kr%iNKF~xB:+̋m@Z-On?tXWnZ.m'2?92{%eyJ|'<&I.νJɉå+dmy2T/9e.dsI:L{OoyBنQ=RV.ݶWղ&gޮsvsIǤ{jsNerOLw8 vYKv#+Sk r>q9>~Wdrk-kOt$қ{瑆.Eyլ_Iծmrf\ro_sy-[29޿I DZ-F+yC;z5>l8x~ՉMmś ]&ʗbU'g>2a>uPgfWjtCidqjЪ[\5o/IwEUP3;$,$7!6P9i x~SNZϺXT\_XT6>X^ۯlR:y^1q\Fݪ{T:;CŁVړ>HZkѽITӂ2du凚5o\ד앚,m^, ;}%?$=";C^cSH|kS48&*bG;wqw6wݰ82-kL9 2;gpײ&Qý;V"]%Mom3n7q6Zf)u+Uy5 {tٶV=w`Jm32ܠ}\N~D^i6M^Adk\_u,u>d׮Ȩȼ>p׏ö97yyI%s'Q'/7W%/%k\:Q"Lcr*hj3g%5ir&|6O6kyo{Qw=ak;atoD͢cHn\xץ+V:GIy)k!K\AXd½'uqcIeǏUKIF>Ѧc&feZācX7ɫKުĎw͎'}|$W$nUaס[RDFVt☬}ȿZ)ѡW_glξLsBg EUԿ)SxS_@Mܲ?Sz%V{B:!)d969Gտ$Q̘D6w+EԵ /vHӒI8*[uXeLIQmNĂvwPv}sfhOy7x7GH)V_m46Em1Vx#{Y#)Y,<8{lބƹWj>^&REz~v4Wp!Y^d䓻ɯr>-͘O]7l7y[2 ]:VIfwu^tϻmz|Il1"IrYWɜ@w]w64tV^}&|vUֶ'1[k赭:v&#z>EV]9m#c5mFV#g.n%fO$ǚ"w"Թ8RPYѪSՏkzZƙ^^͞+N*3nSnO${C:<!G_6wj:Jd$>rJ2g*~5Y$XT󈁍~O q_Jo%#φ_o\V }׫ u 2O{D_[)l8:L^NsU|R<}60 >蓽MU:SM'9?F;%_SdQ/ӧHU_fvy4J[+D׆͓^`ߗ%$G9i0RuALr `73oz+bMm[fɏ]6yڷqڌDPMM"#R W4N4R|]G"s&֡#n|{2t6jpa;7do\$- NS&-[8L~ԉ|Z|ayc+-Dnd2ꃋS3Pagƹͫ{eOw2t2q<bϮu`{bX9|:پ-oYre5mܩ ƨ=kD6s7_Fbefí:6dld|5>9 z鸧pHӭۦ.̀%wegDv,v r]dw95l_5XW;[@KbuXw=?켣\ /rҺkkmsv!]t!Ei8Q\QdO#GTQJ\I%dqszH 1@\Uk3*4gZGȫaSؑ~yri]_'#UVt!c"Ysӈ:_L)vu#A/jd:뷽#Z3q{dw@ۚԇ_dnM^S't?%{Nn-w+گJn.?m_Jֶ;zҙJR&K^lcM}C퇧@m)aqWt)e6u u{_xWnRcOd}}?krZ?5'(fq|}#:4prlrQ-s>]|SIdܻ#W{yӑ)>F{u>$[7;\΋B Ey9T4W8.-"REdn߻ᢦ^vdqfɚ]_y]X٦ 'Z Ӿ#'VL֗cNL!uv$ _管lSrMYms 9V^3rҵ.ǯ:Ѵ׽Urx^?K3|u[9ԙmP$ҍSȫyi:"ʪ1rڧ;~]٣Hk{ȡjoYz׉UUȹC;S'jM ;#߆%cܷǢwĹkIWmPN ra \J-@طK\2隅]O;RykWie{kwɡdRιl~3 u\3{Br8n׮YjԋB枬֒";ް63vO땮SFuJZ9idjڝT'U7%fadݑz9#kHWS8/۹V~Cs7 gدڣb}o^~>tIn%KGTGW|%:!UKn*;~}#6r)vHriըO̦O.˻JW9^ iP[3.ep)e@k:e>\FC3N>Hw%O1b/r:a庫ڙE;5ͧYiuޱfijg.o7tO50ny`4,v(2zZb.WiFIx(wNjpb˺d٦aS&zd8&`;͙rtgǙdN EMju+kui߾65y<;YPQe;"oK2 8lK2a$2_>f>߹1kY`oܰ ߝ&#[ˎM7\xHPkqL7T8יZo>Tlzvߝt7k-j/o?yVIv(b9={edMFoe)w%֗[[1-ǵ_lk(lH=75j"o]1މ*Æ<6>j3üOVV:mt)GRw^;r$07. /d1HcIᖎc}7gSo髾X~"k=2RT5{8tZΰWRV/u׉&b^eVzeGI/y=5O_9EMS|ij w_`5I +Iq}G5p# <;!nppuԢ uSy +Αd}{|pN.#Ia!uf6J>{?kH5U&'-s::-ypu9=ɲ}\8&e#u=PCzT4Q-xcNulHogZs V(mW_ӌѵHU%d+׎hscewom5]}kdq'kʎ9+)4sn@RiMW=$\9Glr:z@8Cքd=N>ٵeO}*WO3mП56FfK5/̊"})4Du'Ή۶h;jsUU˵'xJr =#-;Mi%{uu$Riζeθn_s.%NϛH>5$ԡriq?MeG9wT=9mi6,J]؄$x5ר:iv6Y5  ܑ>|CiJlkbHql՝14xzM3IrkfMG?[,v]lvƬ ǡ3p)c@2M<}B")i#){fLVE&+(FznN5 4@+Y gGȥI/Mes7V%'8^ ϩëӚuMj.No"͞/rJV{Q-OnKpXs>q7h搔y`? wv KwOˮzsfRΡ<&usӕ%ۙ\K-2jQy-hF>yo3'2b-}pƷs;RzY ~\"=].hM!#O @QQA̱[6OS3L޴ԳSЩg)cKN2o7nX[dp`C.gZ&#߈hJF[h GP/<~$iwIڒdZ=NRJ#FnkNkΙEvݧ7^t:!Í۱d~Nx=52v{û6WmЮ{*]U>G)o3γt;u!'Nھ(;-}R{}.<ԖyްK(r=eW5+Sp{|1ruXC}o~&)3Q`c԰oM|n8] M1>A+7ʚvl~U`G/MɱHg\ )iNuv`595oyt>WLΦ/[ bEsm2E7Uvs,W} Qᾎ>dp7o$iv\6fP ^5W[m6j-<6&s=Eo9qՐdUwMru<Ҏ3^s4jc׬]M#݅)sVq+ y]`*ȵ-])tYXrz/9kݼ:.T9րz;־ dɂTyw1M-M ' +{?۶O>A.<;y$>AnAz22ؙdS_URw^c*iLӮ04 XAz ^V:cGD>b QZ,HwiosHmso}$~nnHg IfVZٓ JO5%u_xE' %[*yLzR?ֱ5|Z?):Nlz,#+MOGH%UASBez*ɿN]TрVcG]B~oD&y涕w_R՛|+ݥ؃btn`\1wI½VK#=#%z^ȺVley2J!Z+Ӑ_8]’v=54!*o}ыb.ѿ miӀ]7M3б-s_GG:E\sߓa9]aIVI@ uRP݉޾8$6v8Y;uRsf{s~ԩbѤwHmɤ2Ig'k=NױJϕc.*?s{L,mz鮪qh)dMzB_kLLkڤ?=HfуFomKyCy/=D)]9˭d2Г4;cd6L&Q tjտ2_S'0^%ۏYk!y :%q>7Еs'q i\̅G/9!=w7{[vx {#3}oW%rlý$|Y b9-;^x#4n9(HI^ooVr(y[(c/r4('PbjO(`A]PGЧw'箊ե.#jy.fFJYwVUuSLyWVsߖh7ozP|SŃO~-vm̷ZPQ(#d3xe׳(B-WLhȌS3(pw};`&4jhA()jc(3ր@RܾMje_Ϧ:GvޣqUkL|o\GPԼB)rSųZ3|rySA?$zSxbI%lZs/Hkp,O+h쟗 l)ZɱIFC|'xM1ԽO٫sI+-w@qïhdΕU4p_=mdNrv2r[-2z`zJ426yxi.^6t20u<2nm,/,žd9-=>q%;QA'`bCVZdƖT k%2-2XgjY=g)Y-{Y <7g=YȌKҚ/|nYts'F $8( [~{Ao͏>{H?,9SLN&9]5(m6|`;\kҶ)4TM'#w\*"G3'UflL2KƇeL5rA:;/.!}#*ڒEXLn(iưd/{|+\{IQ86?HdYycΐ̌r"kd#/)sRj.$;9KRzj[tUz|:?|{^JG凑q˾Иq$/Ha\lu%ϩśO-:eftwwVGA6VZDΨhuZ'ȉZ-ao>֓ٷ; î-E]kkٻN* "OT.2varQ8lYM Q0?Nr3uVd3_YjEWĖE_oE"`d 2>=}Tpbf5Ik1}z{37oO wގ_G^nMLA^ىSby:l^]ձw/g9m?"=x@NNkm5IG/r–"ȧS6QAc}O||횓kZuMxJrR{j¼{Չ$1~=&͖k]ћOO6 [\)yGw>蒕%fFxӹ LAZLt_m{4}֍/t'noNO۵Se: uU֒˩ҒyӄHnd³֐ul^|TûҊwzǦ&fbUܧ[ݧCd䑊jyzfCSr\D}bdS5TyG,mlHA;W쬴G4?[7EXSsyMϼRElZ#JA r&E~ *ޚ[O#)m|>(dtQzdzKx?u;jqJ9J{Z,wY=S|ӣ+,Cqg>~O'c_QLNqo2^#B /PҴM|ջi5u=:R9^ߦ`=Em.g(b欴K;mZXFg|x/#T`-02p|j,͜l ל=X#s6гHݱ n{C*QzmgǷ!iYzf-xJ+5wpOZwt\T=iLꪹB"+F[8˷f83n%A3"'>(">B \ 7LO ^Zى{|kyov#9dwWy79^|AQ(9&x~yJ}vN|pe9%4z;ٷ}^/r/fCṔ}Ff{kAYfV6er&-Kc )yYl>y]ֽ *8y2<:}C\л8쁶ȵ伴gDravPy$>8dJ_ɻʎ^H`6guMrRAm)Q@3u#o=$ݺ[}#y. }?z7hdKww~0t75w'm)By5s;;Ȧ:J ' D1ܐv'> H9nx15ԣs俾ҫ^^dv_cg*w6BT'폃TG]OOUTv<9)uʯoܔ:=D u4"_??ƻ;N@:|ji^ߛ*;NMX(v}J_(8ۯT}]a20ms7Nz×uDTuto5Q*rvy:rnx#d l('~N3|+ʩ=ԝ{-iwӇ?鳉tf;yCFmw,ڶj+zƟ.hu:ju-aFMu1ajVϺœIn ^\"1֟>mL%4/F1};$'-NN&UJ2x8wfFOGB+Z1:NnKrɳޭ[WOɫ 6}|nhhZI|[;B(ѧ>FsxwcZ/.#,xv듰r?7Iv+ {~->6|bhkUxKo>r[-Z6O]=.t {642}~]X,F8pĂ.N4mjAd[lbʍB,&4LW߭bt";;^)jmIȳ]Sj۞B؆ϻ0bL2;qd;d1iFyQL6It(3|=X\oA>$뤚-twImqc.}-O5HPSeN5 IDYJ] :5Y-ukN6>DuIuJGƽz|/X8$wr2<8L3ڗly2&ϴZ/8/scB?&o"w;)ty_8ޝ8!d|:qgǪ}K$s]NsAPWNcv>ԠJ)$1}Q\#]&f&"߇wnL+68Oi3֓fn$e} jG6;w~cҜt63=*,k0|K֕f);(fQsf޵/S6zޘ@퇲:(jzdž/t˺߷:<ͳl>FoLI_\oiFއo=fփl4M6m绽"?|Pv~:o_ԲQD.=lLSU &+xu[{+{-=$ʖ.'grW7ݝc)萼wuH.Z[o_,?ڸB?NJL+o͈eOZ]*:m%VSh ٝSF3R"f1i<'"iǭ$5ͶCAN~x[ykddWV4lE_~ nytlkҹb2<ae2kW٪} Hzw4OȪBWT󑌜޹xCK+)$$S}wNw]7&qV]!nKģdھ {MˁgܣSROF_8  ``EdXDEdXDEdXDEdXDEdXDEdXDEdXDEdXDEdXDEdXDEdXDEdXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEXDEPD0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,Eb "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`a !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^" {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ ދ_x_JU){/&MNI߯R*+U5eTRZEY3Z/WHK5Rf}y졃LO$/shw3rJG M^8)+g9%=YU|PkjZ]>ym7bwݥ}v-6D/7}^+۩dlc*ZenG1ݞXɨ\C_ T jNW_I {-!d6rty fQ xiWw𶖿 *1}%Ҍ2 /뙝}tt#Ёu+;>t}Z3 GR=O%.K{bٔi__frK.ס~M?TIIysy?!ntSኯ٨iKqT0qLtdH*;~Qweè%5R֥/S*;:zcz6m>kf6^|GۏՕָ~+؝ڦ8o m:}V@yΦC*]L[~xEW:n}{jXTo0c7~uj?v4:_7]|@==ކɻ'_jnAf&1 .=K8a>Rݓҁ>]?H|kF~Sw,~z*2SSoXo+`zT{#B;[=)wR:~KeGL tU&}[(i0KF(h~9A6ëgmYKQ/cS>g*Z~עxyW>:OrS-E{:\֢3.,^~oxݛwGLsiɖZk}UI 6ٟ̩UCNi+8c)yv2dsbOy+Y^zTo](+!8AoN8W޾U6?+Ynk0P aǼ+Sx |)Y*n_ 3??]>aeO~}(@/V=- vo =N*Y.χ?OUMth9ĵ{>~?t^;?Uߍʩtkblq?̱?| 1rd%bQc_wa=K=jOH߷tWg<Ӆެ7Ig.xYڅBK>Zg-#iŐ .5_K!TwKQTׅ:3:QԢb?S7ݢ7GM>eШB;̛0oJ~-t[耲G~ن}u^63UY5v-;ԋ2pg6l߻~m)忺߿]wKHfn~xn`Gw^?dEq㖷]߉gDMzlUfѕvO~9bq5v{†N\Ete۪&<.*6l%]ݮFjf&a=stwǾqts.V\0s~&\6Y&; ph6U/6T}̽jJSXZ+>ǚ gR<V5%~,VȃSJoO#XNsYmcu76m^w-^n͉Fk]O)s:ah/H G_ Hųߢ_\SO=WO_vR>%{{}#(خQ\=x$.Q6U5jnk7I F#_'/4/L]QR0T<=2K&ſ>吡xczEO-=<>fE'u܎:^H?OM8>Pvɟke?s.B3-*zGh+u~>rok :(ozYq]4﷋ѝ0u:Ta^ugUovY[ xJ` )]?AOTEtX]k$jF 2ᕟK.eZ߉zRV(;wqK*;9&qW+e2}Ǻ)VWqGގG_:ww]rN5{q~{Ւ mxZ7P x~?dB˲qknM.Pѭ "n鈟Q:׉?yp%tq㴲G~>gS~.L׈j@ I./jؼBK5HW=`:ۯe=_*ݐ5]#T<@TP<q]<7vO%]jtT)~Tۛ13 KDY 3t[cJ#[U{:툙?Ww?_1FGח||C׍[`sΦ6=t-kˌ>C功6qs:uQ^yQYM굪Lc>n|e7t~#T{~@'o͍9\)sc^MrmKV ڐT>c?\yx7&6LP86N3_s>wq3*ӌb:ԷU&ӷszQa3MЙ݌x3'~U,'Lzx鮩{~}jn]:s|Q\g8зVW*pxĦq:ilgyx?Egj;Q>w<:J]T)Ugt=׾s+,+oT_2u)VW>|_:d+o)~lF؆b~_g,oYsTk eEjtUpG;;YT}u?wu='anLL.RkqWEP"y;ǝn;KiU5Q-&M1>ػϊ.v8j޷{vm$píQ\/w> ^.y^b+]S{y. V|o˫~g\Ru.Mq#mk{;q;cx. m\>?S3mx:\mgrt ܗuNsYFyފgŏ AEk ;U5rHSOjSg^2f[iPs[Ouګϯ8a~#FюX/ъ\8b{vo= VsR5ڳ"nZ5[ъn`}:3>NT\sYDZ(wb?):c+*˳YӺA7x;դ wt)m+dґ<+>R5_UC:r^;*>nrienkرxُ7 g__nt=QTg{*:㓫3*(u-w9Tf߯x]Ge*}ORO^Js~yW܁N6C-:dɬ]Zշ;Й-9 +no J~O*QV<ȹ:~Vtȃw(OUӎ}{鈯ٙE)Txc_iGZt2N=mB_^r$)Xwt?Z'е -^t2]_'tcU`-(7U1ptc〯)?͚ LTwꏫxtЌrSC;쩖&nRT͚}6h㴊Th)ApSްBCgs?QXc6iFWt,I\7";uIg"Rw(u,~_-S'?˭8_}HyJw*t5?v4@ER_v8s`|X^\O&=FbuQEq]f_ZW1yӿoۻ<^xu^\JOUsQq?/OAzn#u?s$*qRq~_+YcԓJ" S+~u$I?QcaxR]õv>Ků)yT=Vb|lq.nxA=qcPq]HK}\]uz>Jh=Tq]+oo=eUATjeb??>{6ܛaOq<*Jf44 M& JJ24`{5{fTol֧w}~]Ygz}܏Q /g BFo{oU"w1ΑwajeA ]B)*?Uh vcW?S_pIOYg#p%I.f?]l6"N+PQG?SQ಺.)N.r6,-5&D-qǚ+ 87[w_pQO(6"iHZn`pמT!m7uHwb%YZ:캕xcF9,-#8*@(%O]d@Bwm濩QAS7`E7Pِߐtb~pK}6&])7RVymGwҧkEoD ͉&OsF9^u}U':1x{.KHD] vF߯s);[l@ [p\}M. 4ŸF^=y\2wz/pxp>pnCk]b{H!Sp^%n⠁'TfLLp\w$\Qjzq+|xYEHE;iW_?؏Ჶqy큌yG?HyiR4(Tw-U4 =.é2}LcnS dƴyۊ{j\o:Gcg˝̅cep~8iwF$/('Dҏsp)|n< C]!=so zI {?@HszN&T_A\Vq^"i3vV@炈yd}/'TCԮg~R^f|ж}d>gXZ_7.;6@N | =?_$I #33)P hvK:1u z0 c~ozԐٛKS^-{iJZ@њrh |c&(+RP{ '-_awwJ΍3FD8V`( ~&fe.zI?H:@~vM$rd#ʈ]uHCKZ{Q{ m[o) l+)Aē#ڵ(y=2 | Tppj/GS[+ˇp<&w ^UlWE[Z4tT,vqV;|o%Ak$Q\jRs"tdg,mC`e`q3}6s:MT=484M@y_b>*rl)y6DYS ճ bDT(5ŵN mȸ7f9 %W҈sh '}d`qhU8$v y-$^nwE|4|{zu<(5e'}o;X/7DHbT [ f$LU(3MI0E-Ait>_XDܰu C}[ćѾgA㹭Jh{bӆ(d2݇ -?Kղ7**4VOFd-Ofůg"5&t}o03='Yjsx[!w$ u0*.ܻƯs+,sCs?}zzߐ:Y$6IA|w,>AqCv@Jh Ĩ"uCyq)Ng^]q6韖r|B*`X24{'ՍFBh}MHd<8$ɼȦk?m6z].Eͨm\Jxf}PD_2qlrZhԈ-ÿs+.O]Lg,R_i\xT jin|++}Yd!hո1{ ]ÿ[JYI U)!si<>D xxkLL"]f1WZ͎Q rJ8,vMԑ,̃ =/ G^XtԻj.\"$kuf2t2_;Juݐn<1'08n6E@ܫhرHԍZDS8̘4]M;(@(th}磵8Xr @ L9  j *hS Y_ֆsNL:OBFCA e5!S?&Hs aJ] 'dNkeT nC_ Wisّ fA<|]|@ߠ{(E3ԅa8M5@UP:'Nyy px3w ;wD9j->tW#qGc8E~g *QW!_QypyM`G> S,ڧYp?(./%xZu BjQU6ձv[\SV߼ )Ŏŏ#+ȼmRCy H~ᒣ-/)ِAv*6ۺX(%fp˙ 4ѐH§T#uH}3ڎh\y5`o v45Taz_k^D(ʣt1^~u], ou8^pJsC=͑Nb!6jeCG橲isn#\8CLʄ: ;^>=٦yoT@fU7F=x:]Yx#MT x_Gǘ<\K@)>;[cO'@@9ooPyJDUxKDqh q-(^cw,ߑDݏ>urL&mTr?,iuw7KojMt`1f}P(V^>Fzw=tXL^P=g&'eWi&{%dgʹx|?rJjFdQۆ@{͚2p*D*ELyG_r EP%i ȿ:l;> {/dg4xq\cR-jŅ%rpڥKJ8ػb8W| ]J7pJnI :Lp_:Vjďd @a|{x!(b0NBڴX WS횮5efJ6T_(eSa2(HTf︺}/fjM/N@ܖb;2q[;3DTdzrV*򿯄n3뤲1];<}|fdp)ֺ~ ر0/ o6w윰2pܠO-V2['SLb|KEa!#lg:ωҧ:Pm\x+-qϞU$1u4)뿽aAj͇5J{0W7}MD>/@;=V.A n@ʿ~ Qn0>JNHaAR}-Bҏ)7"6Cf 4E2Y %8/ԲEÿixh[qVzX{\]Ge۳O s^ޙ j{qgPJKAX++Ro3Yw8QقzңPV;02?^Fqꈸ#7#tS,z|r4RR9B~K PK999i"~i޽n@K#e,ɼ$I1=Ƹ{kQX huү#-}:> gcζWmetiAީP3F^O֬>_ҧ //| s+=xg~RxO_Cn~3ŗjKGH7IUDJ )0 z\ܿc4JnΧ;O0RgD},+153 {st{~PsQg~\t Sb:ƞjB狰C43,ބ "s=6Iu&@EegV ý|o@WzPQ{qb9Y|Q; !A"j"՘sطN7aqwOmSxe\ƚ?ƥr(yƄ9Y x)m q5S8nj سaf<J Nps^`UqaS]FW <4.cpܞ䄧{zý21\Kk‡@h\_a)F~CBK{]Yh* Ãv9 j嬛9V9ʭf_WO繐\Fh#=fF?.foBiO?勼8`= QwKt4U(:@SJӤ>Y5s| Վ]Ӯ<:JjMj?铼qaq15@1ݨ~quP.Yشkέ]oWWKNEc%@6>G}~c[Z`=X?jE޾PFW? 6_u8.Tp-0d潊Y7[P~-DWLXsc6e.`X|vu-•T;&GȻv+?S4/ BsH?_$CmgbxѴ9$+7`S;C:76TO⭿;UzK~+6DyH[=fStgy&_Pj%k_q^U9k|!ỷk&ТnA ^]a)^4kQnVoO~wqmсw1.5kvvL~`M3aׇoPcb ÕR٨p5+V(NI> NۡՆEbm*y/T!^ZҳLEsPcBKH1jpP xo+O0EK|+\>]Mcu!#g0\OLekGqը(Yݵ-ĎϬEqHC9<(q{=nD B t84dn=p|0 @~n ?!H]4gԏ]iEy~e6Cg2/F<|QWQ#~kz^Uy 1{.G0-X禚B8!;As(;?w~d@87"FնvM}v (1>ϳf8wg }Qx^nHŷlU [ѮEyH_D'n7MM$nyO_ KBWeq_W1fiR6Op[rN^S/``Q#0N¯8/o_7-xS~SɐJI愚Γ};zmP~}ITQ̨yZr$,@8i3Tmp9Nц)g.HRtD-`o _f?يz]}o> έ,Eh.sDO cՁoY*KMyʟb_ ȞiW[-bޞ&WVT|x\ߏ"*@=2{U8BtPdӻm)/wpWž|Jfy83>[Y'4ԭ<5Dn7B9oux? S :oU6~r`֝WHԫ|!\[EypLn;'Иƕu`-׈tYO8ܻTi\*$' }gKjnδϐ9ǀ6?dQ W_ìaq}塺|F}4, ݋_7ɏyU4/Kл^xa ҩraɺwX;jר$R-;(^%Pɽ:4=iF;qƷ?aa#A7p܁۩l5t:ˠ{{dsDҔ1GH/nűK]QST >ځŪ D!SXصSU&P}Q-:^?^wbΉQ/V@H2=aM${ґ(U[$!]SO5(I<_IFQ<#f o ~wa =Km|U$_!ӨWP?O2Ao*>6NQ1gis>^nq /VkĈ~;B" b^_`[0@?'{<`ک!N[c~ũya#AE>){lG* jR=hd 2FG ϯ? 33!ph^id\IShohtž?B Uߟ ᫎp~F܆ڿϸQcOBC< bo@:?$2vwx@rԅ5 {fq.i4/]z WOPW\J@֎YdH?sWvtD<gesqb{ї͢>G;"BșWu3o/yV,0&G5*\,IY)@kPFq7{8pH%,>4pTٙ yÀQsniޠ+\/;^h}V!P2<ʯ:G?xC/7ȩ0 }n p]j@IަK`l ߯i/ݜ0w>h<߮v)[>T Ï˧W >߂p[ܮj>Qמ;*:qcEnVڍ@i)@^{h$L<^>uӯP,ֺ_<ƥa>u/ ;ZX åMtɤ{mwQ c/˲MXtB(((qA(;Zuwl&(ZFƋ,uG^mk-,(\Q ;|Fhi*P`VAG$eUHZY ޾8Oˎ:hR䷨Pa^RŕlH?nO ?~~ت 7%~Y hm+p>w P7}bGqQe'A.&'}}%x3=7jw^QEjdS1\6aE{E0ڜ(~3w6SS 5uE'Ci'WX1P~[ Y:y=gFA-_G>Be&/s%ϾݤOc ''^]u~ G Pw4x+>V8W< =j[Sjwm[ [sNތ$ x} 8og{zlyN Z&_Xi ;Qe=ީtbs62\U xZKCd+OQZdmEyG6y>gUv-o XMI'%[qe9[L% ;ӼLR7}".~Cq ۔y}Zxgplȴ <^u O}20D׿"{q5cl! Dkvv\,ClIvѸ>^[A{tLbtܶqKa+N-q`t_y<1_vmBV"BR ܔ[Vqyjj!z%֛~IGUFzvq-W?~ :n@:_GP,z~{|.`Om:M ͥ5OB'0nȗH+ep{|k՟"RVp1<c:'PNuu_|Tu"qsdQeN†O-K9r|9Y /TC#gy"6o@Tah {#?2?yq X:z#l6ůY_8]R6ecF~;,uw eykG%ASX[ZԒ RNƵ(zb(M WXL@!e9;?7ۻ\hֲ!3{, Uq ِn[/Zܪ=g']NsvB(~egUi޷a6Ů_gK٠g$JM9BbE+!?sȅrcwd\6dt`JӯCyqunEjjfN%eT;1 }е =#WS-U T4D>s]q1#^.̈́:U璐͹(ߑMθQJl<q~eƷۏ.pƆB9݌#__هWpYk_%v\# F|׺B&O[!y_k݀Zf\ǯru BC2.ǬVTd}+5wZ]se@-u L^c.2<Ǜ&$Ⱦ s_A1a/ +D]4e gG_2޾B$AϬ=xYt'IhڴAT'uf$/`%u{GH,ׅE1\Dˇzuc Y{!ݚo]ύ>f2# +dNe!|ޜj2!C@_U#ndnq1kzA 躳JO!}1\:_R0-{JgHܿ_)o0?.vJo=/a[ kOCۻӀ33.j$[o%wl«]<>7=adR?}4kpꑍ7~oxI)ⷠ.ԩ,QWl܁ZO&ȗ++ѿ;ocscb&]4{ᝎ[Fk,ym 7cւ>'iC97Kg\"]`/w'CʕKhj6z}zϳfnu5: T-:0=Tx=4Tvrl w7Jn^?rCk4h;tLvYCyiQ]owF!~A-blaznWҶV=c(6fL[^`-`erB&$ut]orbXyvP]-bgg?OI@ ƿz7l5?>Nd6%GkxicSi7s?PnDۻ`*d7:MCfD3O_$PP>\)O]P*Y3 B9>\g$ϵia[|Y(?#v NWX aRcePgI_AÈj9RSq.f̂<ҊPߢ&sf37؎p2%ia PyXҹ=;7ژ/}#LKr2ޜDžʊNz,zl.yUQ[Tf+z@mtA='3KUsA[_didgP +WSw3/Gտ ǭ!\3JsFCVwPnP[ȋq󖠢_>@~з?]b2SfYBÓՋdpˌ~9ne w@XEYFiC}rm48/HP}wN|Վ'@q8fAU56CVsƂ_SB.K+Ծplp\]g6ǞŌ_u {p>Qu!N6PǛb<ృPfl?J8ۊP3}F1ԫ}8֧/[ #uwa{ hR?GMok0\Q{^q`8@@m'>d>%T,wWv ~wA/P_T ښ񯣠j\KPvYVn|\?~lMsU8"q:ǥD E\ElU+0T,^OQ\8y۳WSśш尡zҁL 847nADI<<֧P<+;U'cۉp橿dtP9Uhy? 鴥6%x16O$'iӗ K%UGg }o ={>lT|EH#д(+E&Dh:64nh f:24F"D^ލxa'FFlFU{{O4/yq߇m/ybrD?va8 ?tA1fˆ:w>dП]um5}B8m_Cĵ8 ! [:5}="Mn G4T!hv!mEȯE(?q[/9b rh4"}„?ûMZ\<+]][& Ii@uH=KjZ+wZz; ɶm/g붅L:Ձ<*@Xjˠiqu&^V:)K$MOVeLy{;!]3{40O^-ܻB}]Ńq>wlPf#"q'~-'; f7Z.-pw8@;* H}&П|dwCګMV~n\9#8#y3w-'~.pϼq`>V0\TWuh!g}W߮#,4#WxVZT9(}V>U) &{!cŹ" `Kx%~e$q rvI^,r 1 + op<:`\ٖjO8P%ŏ t!wmVUVCwnuW+=N۠> pIG`e)sz^}3} Ҋ=Bgzrm :Ǚ Å +O6`:RDh2k% z~Hcw#tpYf;cZQԫ8%Qg ̭WmsO_f&0&eyqSp\uX8O%` ?ρoΚCYe W>?g2.ɇYoCɈv{X~ G.[tԍabZ|> ʠ|Ĉ9|4qnOBZ`-r+DP{-۟v/&zUHKN`{xTCc|-t]8vJ7E:^𗸨J_ ۣ?4yſ>W˖@OY%с\g"|h7ҋkǔe~T+ڞVyVAzX ?oGQy$潾}ܯ>?e((c ݨ Fچ /~Qn=$#WN|챏d>xDQ\JA'% ckգgx: g5)u~OB, {Ղq7 @G{°|/W'Y:5w"0[&nCz}q+~IEs:D;dYejvRY5cHs) ,.%!~ܘ¾l5u _n mmdf!aΫG?7(;ZEk\;k?_?>'8ddx4^S gF~ђ~?|*%n/uNX0%N4>b7$x8f8Us{‡1rtDVֻB@|7{סzˉt%70ԑ~sx7M'Se௻e:ñkjepϸfs Ĥ,tS]5 붌dtA$P{)zx-=' \~Hy5m/#vnLwE(aE[5 熞 7eFϱ3]OC*!u@u WMc*#/ e$م+1ܠ?_}Z>*v: xiU1 / !m.8rc`z*F#tB"ϦCx>`o\H+QGzd0J _y>qKs/K'߇DB?>ޅ3c֜E{̣.;N;S17޻ኸ|[2|z0\g& ;2R8Ӑv,ڑ@pU/ZptlP%5pJU,i1H7o|rBC}u]qcDYG%8LZcECoQ8Ffnni>j.ŭs> .F~upHܿ_)!QPSy`d>U zt=Zew#R6K('`8n~0S~.cE^c]PryG6 78n+ioL[=ҿ`8"5dR\^.R$#?;|ן&hG9u 3/Pzp)y86pۡ[G[|2cbVCas1\4eCI@w[(- |[JU]yqޑHj< 5ImnH!b?TLt(o\u1\mbS:V}foPԂq>>xdBv((n%Y@ oJǾ݂8e %~`tB!(LO}n6o:ws?|,;/CGKry aݸy]ǔK~/TtVGqB? `̊1EywA4h;!eqwHGMD08;fNFI#`#_o}S&JU_e3%tR6mS5ϽDGC]D5xDv{1m[@INbvLo@<*a0t|u$- ֺAgs hk^@qO :[Y4ſL6`xj:pEr Oh\zKB으 e8F_lB/-q)4~/m=n}vYڳR 8kQo)$/ҥ 08j_;mMA+]Y`svD-Ey>sUS1߀s?9cZ@ؔ s?Eb]PdD|i$!2V52OFy)j7k꧕JBYےx۱%I#ȣan٭@o6~&ǶX<7,$_~] 2I@)>]6';dp,FW~o({3wdu9+Ֆ;BGgwRɇ-g#=2uY;v0F(loit?jjo[q[tV=oN>f$3MLym$мKFD>1*xuȿwqy5Kd(#տ33 zNx]{Q=s8el쐎.|s5 ^X><~䮘\'kyTO# 1h?<H\U֧灺~?}K8}xh)~ ܸ|y?/]J`]8f4[4Óޟu:}H@2k)݋i|r[So/|kxy{P,h; =pٞ=3 OQ.i+4P":60hH{t||u}3hxi&o M9*wݟWn3}e\XhB]"P\PcY'Psk@9^;/?/[oIw=w60ʲiއbiC^gFuUŠޱζsi[<.!k\fW| c~\׊ThXo_o)Viou']|!\;΋_`@bؼpP1 uPSf%<:ޅ$kwU=}P "٧|>pMJ9ey" <} 8q?Ti݂9Wkn}ͥ|^q1 ߯}{M Scp}a0tCDg1?z6m@/{,PkmxAƋ{]ʞ|/ܿ ɰcgyKK#>\kJc(>..ؾsry{RIV@i5K$kf1 9lr5SwGwַ7i.HǛuoViU:ԼeyWiIܣ:uG,}#O#`ʦާi#h %]~*2lA8;zCSw19F|0'r|k]k_;FH*ݚ8YWKmy/_s5ޓ ȸ:#t| i躑_L}%JjH)z;n{+wO%ߚ/S^JČ7~hdٽc踠gi7C0DuPϿ oqK0](G >_Ut"b{?N|-5WP|2wnH-w1k`FnIC%'F=/JpT[ ,z=(O;k [SƳf#eFo>EbMf>EǯDiVW[0G݊y0jS_Ce- 1;vg"΄u{.(U}3ОgQ<2&"BCEh{uG~Pe@턓iY-߽+ٜ _% ZsZ*GC+D<D{Xo/K\%uH2O6uϰQ;Z Doi25w!lBӞ6}BqѸG9#>6;Kdԭcaq_ -Gc(;/\ ȪC:ĭAJBdySύx_;qD5VBҵؘ3~Q4l =8 "'M& ^itw28"Ò/lL$q9ƽCO*!NH(_H7kYu~0+z`Tg'nҢp~~/&]l GC-3 =~=<йL:\1~nCիDHmGkҭ:!u6 ~y}aHγPaِX=%$ u5 s-.xRIu1f]u_0KqmZtp287f `bA/Q7qPUAD(ǮWpvZYtL ץ.<*ն }i!mw6:hE~@,{ckh#])QZ YRArDkpܻSPMwn }-]C s.]k{kvOՐp<ŁG&B89(muL/kCA<㴆#dnYf89^cF} nB'v iomVᗡP5#wvҵkWAbu8&_y@[G[=ߍ.k_b=wsM! (v ݿo0kH?i5W+{JHWXG?0r;?B{Gv:wj4r3qndםX>ȵ<fWomĝF^+qi*~S4O6O< ٟ}[) r{䴠~Z8yb0o|jd)TK=c/U0KMI4( ٓ"z):7Cy䅬7ڰ,bqt\p`_Z5g^\!y! ,y:zW5'ѻD֜fo.DA\qCV'~x;zC@7)Cc燚psV|S/͌ w_|l6l|#t/kU` Hgs}le Ŭ-o;B)U%Q%F=#u2b7$4?x ԡ&ӻxByԁĸ';Q{o_wHqx` o{tor- t׍i܊Eg,y  y'?!\ p4'mHAT=KcKO˹5I;Ee_oy;'vr EkFqeKhGlaqP)NpB fע<'TQ_BKwz7B7t*}ND<{־Gg9X</sO~D8:m1z\ntWopj˵樿<_Qh]s`ݘ{PoP3#|)N 7R=F;JlEeC fy K(%S6oZ~-@:ޯ'M5?p_\`_$Ww|9-g?Mveb? ~Cnom|r.^~6.J)GB¶"uV@~ KvLKA<"K5OL3/\jJM#Hs^Wi-TIBI?1G Ur}˟(O8rP>d:qʸky!BtIT!#8Qa(/I(^Mf>Ȗ#?𥯖-DCً&r >Ak zMmsԈȗ"ۻP\\^ %j{Wz jx pk ԁ< ߀|nJZ}T%h'PL;+ixdfgm_ޏA6bA_ewq}#VƾS7y> "`m'Kʊ3 [8xN*1r&yg2Ӗn0/h:_Mt M\?wsd=Ǟ[[>C}CݡwF_4PG/PyC-۠zNPjFy:[xym(́cgX7+51vN@}k~O ˙@lnh<Ewu$(_ʬ+:̞~`zlo< kxw x{;^Mt,ae'[8rp( xj9dTv @sM`B+ՋN >| 3Zx(禟E2=̞8rቹ[z|v`Wz8t~(Tjzd9ߗ2->CT/nTt4Mskӳxob?%_~#G Gs~>>C GsCuOaQa1`Oʯ+|\kݗ۩h?Ÿ 6~]!I!8X56DwERW}6{~>jȫA%!{lܻ~ sV}@qv^cCfaҺd( 094zȇfd")RC&-h8!)#"F ]'m㢦o]x6$m |hb;#E\Cq(o|fϖjsqqEYP=.".m_g,@/lb%ċu_6,<;k>[I-G\cR`\8CPD;$;—o?m{R.:/)5| W3{-G9r]Mˏ'APeXCҾ!Mҁ}@l})wޡyS5 W*!˴nX|@Ϥ{`T2ϊm;2=bNr jo}p \3}Dsz\~ '^`?[1fDoŕfN;ٱ/L,+ z{݅s+=v7:󆘍fh(/:14y/}c[xn'C`n/ğNԕ^*'|>#p xO@@^4 5Aꁸ+j}'V:{D@@Sv"N}^1AK'} ?贤.d 4Hۂl?ůR f5/ kB'qBShK(e/t/!fanrx+W3>j}Ԅ4{{|^-|O ݸ=8!7tnI}^ 毀k]֐ 7:ה9]Iۛߴ/߂~z^ļrF(ք Ϋ" 7{;1\޸oyo:Bb~(cbIPi{HqǼa sQtzPȇ 4! 2L!𘆍O ߦFߠXLaK H5R*g?h>ԭ@|B]_6:,t9 3Z->GV %zQ(ij1\N*@J$>f%%gwvg*!};&ٲ1u],! w~ڋC+Fސq}3SI}Ir眐Z {8@@uh 6]ڻǡz1Y:ϖ(M~pV^AR3ޣ~ǡON$jn0Z/D}~ޏS_[tzSy}*IpRwk\zS߼R _C_:ԹwŮHF5SטzK;7ދڇ&@rCa/NTUOd`otk^!3J/yq|(m- _%أ8TNG㲄gBh|1玆M[w¡qbÇQ\?6Ψeh^)Jᕇ`;X#UXqtW~vm9Gu#(_4sɩ'mh!<:&vE*i>Eh^(?t} /kr{2$fa`s*q##)֫zWL00Uߞ["?ɤ_bE_%glaWqlvpYs mZHUKv~ESC}R_6w7s~`-x m75D+PѬ`^U嵯 `>h8Pt9I$n`;quFxG9{| D-6=`o2_"V f 9(~W~ .fzdk*seY/ c_4CeK͆T~H b,i 6W>ɤPN+'`h_'@FqN#Z{VD|+ E&IfJI8/ ^Wgm6t@a.Uh P\/-|qupYT'풡c?4ds9}P0#ε,U<߁|MSt˝.Zۀ~XTҏ_'׻脏)?ި#=|x uMtŧN68nY8Om?J+si+0.,1w& '|yg$_WC:\?'qy=ƱSz KM3ao(칫nNyɣR,Q^TnB:q*mͻ1\W/P=:Px^y.>f8%JkL*Zk[5#p>TKB>Dտ l  |8u1ގ@/yIɞ=Ǧn=pO(h^ 52k`|gPQf|y# e |=xj C o)NɴP}qokMPB4N ?/Icc'!Owu8enʇ$L 3(+Մ.Ts J@Ʌ}w!j| Uq^Go l͇>w/yz'ו5aPbr3[Mh,8(4t=v цN ėJɵt>\jΘWErRhԼr,ҹgބ Hwp̡kn(J ΞzW{vx pL9Woq#qM[y>17}CC#_qǝb`U~wkP| =1+J@4aiC/6xdy8Vy+ݖ!XN<4}gݖui҈y]A(zILwޘ` }}*K/%f1Zgx=IS>"SȒY*r m@͓? 4P94VyPpݏ_a#xd_(ηh))|1 B3ztmQgy3*I]ϭ;Zn@_a< ,,h6WE MΓ0UxSz|ao/ Diǹ!w<Ew;&dH\ʗm5zKemWCF#ٍZ激S(KL0@OGb #Ln\:h~K+_6S^Mh;8FwEcDǹ24R}t+b- /T@o oDKEO ܰ Q܋ QJgz$bbذߧG|1e oNӌY2=.9fw8}5U܌u2 p~6ƫ)ILpsϷk݆Muxw?7ti)}Fvx{閻D6 1Mme\om׶?ETua:e:(ie5?sWvEFpF^x9n99y^#WuǺm;r (2V#q򿧄iS!MbC+.GvAz`8?"o}~t-m߇YL7l^u9 +!ѥ'p{N<+H'j%F?}E|[\*Q4]|:(窽~K31XF-:8VU@qs9x{l]P]L&_ant-:;~zrzha( i߫kP[EC74Bх;Uq?+kWjō_bU#|~(C9xu"4OXQc&Rm>!sK a GKeYI a ۪^r._#Gnu..ǶAϻ{K:p`T5`Mrjopr'J*tu$?6S$vޡy^5ד-Ck5 z\[ql61QbcO[hLP_5e!Dm=\o"CO_[cWd=:QQgo6jq <@ ;;f\*1 <% K=($/%V6O"h0/v65In8]&g>riC嚋O?8W9CyKP|'p̵jUohC:OK ?A J|ZrUl(S⽄;=%b7p=yq$,z9r|Ҷ{ox&zGxf9`NrW}Icy -!2Ho*e|gxd`-EU t~x 沧gP{TSpvKmÀ]ʞ tDMnnkG#LeqJ̦i̫t_  X۱϶"#B%F5G$sZQ\b#sMHNEe6ӳĵ@H(a<7uj7oXMG7qRµ:H^Kwc.$}o .yZxrBdx2{!"h$P/lOgS{c7x_4aY1w "Q<-laH0}-'h!V(c/$$f</v϶ :6]0Њ'&W< ~C/;;^cЈBba 4v>9aܕ ђ 5_Wqa<5Y bfG:z,7@b+H-a&iS7g@|yQC_܍&Z.@,ИD>/ie%lo1K8@Pq^1h%aOH-cP$>o旺nϯc6Oq%$Οv-cHÞH2]럠~Wںf(bĹZPWg궶2^/&u 7~_Q [NX7q Kν'VAREP*-/Xޣ%V8n=o3X5FԆ4+[EՆƠ FoJ/ =\SL?;,Hs4K)l>n4RWm@{Se6%m )$7F:P}?HWRůok@ݥ o./>alyPϓ;Sh ۗu p+9^Gcw֖V.lQ@gt~8ߥ&P~pbiw3R7=(R2vB}Zk<'[0Ӻe한Ѻ 7т|0P ޟ^V=+Ot~yn>ތ"O $KqS\-ϟ?J5Ac mDSåQOH ׋XґiRd0{tk rBLR0W]:nM6ۈ#|nBY~sϷR7,׋7طm7d t2}u^[_I*}4l+R]Xنey48RJGޥBNڂcg4 k%OY˺]!:cx|2m!1ޟ]4l=>2e߆`[znm|ӘmYm$]GL[G'όZWUw"& b pTQ6{qƥ|@e`}^yryM\5h!XL>hXh_j^NjRV]t~G;\x <5MkCi)ሮc cԪ<[wr62w4عMӢIU NykШ@'-қ[oE]ڕlhAHS҅l}o/k* h(_iֻ8HO'zs̡Y-:oBu`NuOcmCzz˄O\n7}x(8<;~)%NKбf#TR5H(~ Z}=pGD> b;VaZ7mbYM"W;#q߉9>2`{^{7L ;\OU4S_b묗5V\۞之ד&м~S`cg5FVݏ܋?-t)_/emeyʟ(tM9v͑H"Ϳidny4fqr3* >!MEeP].lzg~06W:kH1;HωGW*[ <#}y32'ʪ*h}t(8Wkt#dL`1m4jܩe$"NcѸ9U2eSQ]e ۳a:z╔ {?1ƶyuQ/?(?.a߄a6P>4 o̜Odm]"|?mŞdOEPkpc}C4W޵nc-ܕa?R]#)(_ Gc Oգn)|cw&jl3uxd_GtPi`RN_!Q%(n:A)iP!Vsg'&/( F( ~c̖$gsŗ[PcN^>/! 67+|KsaU!iKߖs{tەܚ_Dѳ%<)Nc_Q3\o|8(ct!U%_ 9^c+_q+Sd#js]O9+?_uֶA-vNdS"]u.[EԴWB尝02Bgo;e"y)RWz GC}'9_L*S!*Iϐ^3_X9-s<ě ۢU ٸTҷ>X|_CD3Gw@Zmﭵ3%$H>l"8apU)s%]}FE{<r09ϲv*u;dC/O Qx`oewY΁ ŝW`(8~q9Pl0}*Q ԁJh; ꧜p,;̂WrΜuw@_Lh%RV`' 'u׼-=E;sCq}n]Y;0Țwps$'!7=F^ W9ӀoH@v抸gCLT Q <&%2VN}*nkdZ9g婫x]Y¯dunkHM|]I8Q)?ΐ wdىڰwh_ }V.NzyM\^O++C6xG|gNuޢ}XYC<.Eܴ-P]J|(n΅;łbgPB 8Ǭ'Aům‰Pgde( 5:zd[.q}e)3'"٦E]5u1fPHLQ OMIZXdQ/yg.q2϶3BM5;myPt|>I!0t6^J];܎o 3u2geʞ\2q¯Gu- P~s|P+Kx.?Z5oԮK7Eϳ ( GTU ^z xwuL(5Oo/o-:4CbtĹ aJ]97&Y$Cѡ'|yV6uMw6kt j'HL) E!S-a9fϞ@3uQ ]Թ~ʫ*AqOXO`&A -n)4.>5YwnF+E Oi|)GwkWQ(A=}{HyVSGJ`5u ҵpmzS"m/ 1&Id=O84.E:@FMC<, y= [$[Ѹ4hr+[!^]{5ZpfB%O%,>@/H'6#DF^ř!Hug#h8w='0L8P ,g ()O0^krֳ^\꽹ktTw[֋˿Ԫ>=5zo)g/^j4J8,?l=;8^u6ϛa|{$Mxn'c鐛?"3LDְڧ㑾#ာz|~IF~8 P_4˼${nfʷKM!i7p\i{H[X~,oO_̡,JWDM@`XUՋj:{Ųߨ d$g% %Rs9s91YYF zYU=]v?עb"JjERax^WG9Ԏl껡In+>4Ār=hh9dn~ ,[>~?yamӓNc=B_W3uC{k?/_~nj]+2OǍF3 wZS~ҝIFÚtj\o (Vs]eO@.UwzrɎхF'?/V_f;mïB{h_.3{Ʒ[rL3,`9u{(y4.y%=\p:vӆK?K>ݙ&ƴhlOdv^H;OC詥tAtJc9Bł?IOxwvM.՞y+ nq QF#?w_O,O9G>M_fκ^k߇S>m;u3eN/t~έyq~q9t';j1wy~@f}QR{mu=i-fef}[]c .@91AЙj♦.kUS5q5Й=Sw׎]|؁"E-!*eMHy=*aK~C׉ZPļ#b 61uۏLʳj|OQ>c5M)źI|0%< vS^3so}%c73|٫~oj9JKo^-Kj!U'L %|󡻺ođb= Srhxvۭ_t-[O\[lڟϐ;Ch8i:RUiވ"YA;~쮇\tyDf1D~tznK7}RK^R8ճlsD#;W4S ~C_¸I/denwS[1 n{ni߭M |/K;SDvIq ?믞O pbL۞ձƚ'tHjɖ7uP̻Q{ב mh>jlW26o嫊 5j;Tׄ]KKȭgt'9-%UϝW/fՆNhx258}ؕp5?9y?ي >7?ѩKf1^V,W-g;JOO\Sׁ\9-20w?"]Fys]ˌJD:Tur8x?ܨ=U[qw<(v6#pwh?vnۆu+9_7y6un)gE??OuTHi{ju5)> ~t{qM F:Pfkn֏nY;bL=~/{>wMSGߥ{m]S>2J֮rwqVvCΏmPiwfÕםnM=^QOmhǑ2"w7 ,?qGXMݩu@/%te+f<ܘ^l*5Rc@ϚӖ.H/lYt9+;oB[cMmhvњk'7)JSƽFiZ94zYʞvx \E鿿mZ]~]zۉoEW:֞\ su'4]A^5k]+j܎g9v$ݳhί{0e%NRVIGdkAk_(RUӕE-_擃SsvkOjcNuq6emXӮss!(קҔE쏔U/4uYu9&ʌ:oO75P_&5}MJ^bE)١F,eNhRvCg̓e%SF tsnߢߦ% W[eqíta\JK%Q3V4SeL~n{Z 8~giMOߤb#MIg-o4kj{x#{1z: s&(n-_2b&z"2~߇f mWg[=!eGi|U?$Lq{~pϤ[- p8}S=κ2 Hos9=*{::Ra4z}gãt5hn-ͷ~kڰt¤qj>:xwt߼U{k>ψkBjMLc0uj^ݺMiU;먏I 9?m}y%sU^# }A'9M|x\ۿ-dկy= R^G2&[ΟUݵ:^ֺ2%]2]QsVɥ֠S3*X:N?7meye\/>T1_ZOIMV܏;4T': j}QzGs0JJKRdtM\g}.%'ۇ<ߧ=*@5_Z|ZRvjkQzύiңA9?^@g>L1 E9'/Rlf !z?kA4XDjs5uWƎ KC==+i֕͞˺C݋lZ\xέ>zɧ*Lܗ_9GЕջL֮3v~ϸw]ʻnEҕ4^z g'mWԏlc_ӽ a (+0r_E_짜=FBicg.lB}=?V OOLV>i; g^}\wʾ1e׏˔5sDa!USkz3l”t}z;oH 19tK״OKSIvn9su6dzheе5\uꙡ\a,46ȳDCӴ=S|ձW7K\G9/o\BVWb~m1c7WmrK(C棛kn 7\PN$'ӕ6aߟ,ٛH5vbQ_lҩC:1]m}f&j>i=K-P>u9N2F u7vKnuzyر{gԂ X͟;YMgu/'';Y@?$ɫ|rj^#u?&S_P#ѴMᑳ:kS\=wdcʪ0Y;o{ :d 0{-dR۸~~ xɇWiZrRC/j݌V{׬2p7{nac':nlrU'馫WnSSLwҋb}s_E|J}?O#=3Qw^{,L<~gu]MUr$>7NHu#X~2W-:k{P #u)ʑ,jW]{t+zTs}PR :r`JW>%̷QC\S.t|ʤKxs,Z4@ni$ѪNʙA)f4<Ϸ=)!F]_2yNؘE؀UG)=Rͧ=ӼStUӯ~ͺptk6~F:yGUnGKsӛat԰nasxh-eGwWYmm~ 97t:Nռۧeu+Opl>p-ۡӷV.~{ ݯ2rx{v).]oF]8gOIF 6h7o#wݙ = ׎S_`ͯDz<~ RŪ@{jzieJRef잚\_&axǐ{ԤG>jg_`zܤgP戊 5n(b`hP϶XǮ=lJ]ذ2(m_[9ݒ4ur91e?+#LDGVu)x۟ݢ"o^_j,ow}IHyZMɬ 6ZTȻc>ߦb[n]cGJoQ#WOӣk6Vh9<89 cSwsM\|t֦~ϯRk_^"wyo?|lyuo)cQ{oNiyDMvL|CWCM]_$z3qnTM"nࣞ&^n4#\kx+sk|t1.\|M}?l nNjw~KNL,oE=Q5e:d^͓Æ{_e@6ۦKߏo=-QxayGJP;_9?ΪWޘyDuN /ԲS͔~QԴ%]q7ַDž^{o;|PEwuJ.#m2VM=o^5<HMdQ>RO&iu;o/HwK]Pr_}2t o:iY"}_?~t/s:N!;T/?MrFqKo'2s"6uln?4"f:p18|1N.,ٮ5ټ- 8Cr;T]'jv]n\°?KK_,޷*PǩSm]zacҞ zGL>mK-b}} vQc ux;4/b(7Tߎ)xĉvdpw0w-Qg2eHBMx3?lzKj7aJk+~|}^n:Rh`eY:3d#}jq˧ 5zGRvZ%ǵJ6J=w(}<}F"Y~]7j9]M]WlO^WAqf YMqw?^j{Ϣ!.fejn〪~QvIuVum˵h&XQ\w5TH0g:M{=?qB)ȰTõ߬gsh[k'vfv/O_Bu Ng|N mx:k.7^ui90|QWi]mу"O6WlܪS/\Fu/ߣ&-:KRws{ sR:yԗ-" xU#Ǵ҇5..?hR3=SRUcI6kvuɦ{ّ#aǘiZ>jEۓu\Zq83>AA:u-ҊըG[;C B hꬅY<;r{d~(uƁ#4uVUOUv#"HeΖ1)+ڹuS*lɞgdRfR/v%7G^;,-mZTS;BĒ|gYw|u+VoW{ t/wSd7{v~#-֜O?gN4>l64hp>ei鷍QUNhz@:ph긍*~nҫI5XV" =9O#+fVb-m?Oogxl3| Y W.n}55o8x6nkM&ƋQhc rEz t=s,r4bÌTt#>j{ߡɫɛbɗ} c5u&gY<.e9v*iuHz{VPhBa!cRB@l;&#ׂ#dE΃j5uWR'wrA8y%Iӵoȷ;,>'7̻߸?tmȵOO[SXcN83yZ֚;F媑e䂨=NT4}h'O%5ϯn#oTK ۿm~M_;B rմٲd5+ *9T|MM۬疪+e ɪ&5v|+Dt'[M]`Gc{C'VSg]ɖөote<1An+&nM8nY_cYWtz6ȽOQ<|X"ߜ51Yq8ӣsoi<$7[FeVyi>"yZ䳯W{IQǬ%^kE:MQշ9t~0a:'>)r!խKW5Wuy\3릿/,[\'zguG/w‰~qI:~IꗪVn;4&]80rn}ry,eʃ $U DUu]}"-׻hd:~˫c: ?ҟ_޽۳:Oͬ3b_Pl?Go7KΞ_S< LsY!۶&OFێ`~cqJo.7:wu?|7;ǣѷr/É)RWuc)d%&ag=7FdQֱ>=ns5\bs6RD"/]~q.؀t?dn[x&O͏R6OvN[S}9't,oѦgOl7ڠ6)YGaUYV #y|]aӅg焩 Y)'U5Vnj|r YOzlp'le#//yz<}/:ء!owj<6z$'_û&t|=oT?V_gַ}_9X*̍z-۠9I3dmw{=w*zY5^)>f!sYEA9~o^xyg~sv; 튊-#kVߠ+x邉E>v/zn5u+G'Kx%=U9JMW0fjqu?%l_rs253{s{N>-6y'w w-_QSK}\OԨ k"*.mlE]E\1*hן߹i)ܯxΠC/o[#Z^̥"#Ωar/J22[IMՁ[ "-G^#VՎ';6cf'n]tc uf(cNw57;_}^{5u&:1lpڤ=ްG-DZF ;jcԪ>td1Td*|k >^5̮d8t,YtC{^YۘS6_c>@dM4u60忤"YNK.jΤDSمR^Ԏ:X='(g.T&5ulgZMDnF^z4 F'̵J+{縝a|u΢kGqNPū.Ϻnْy׏C gUJܶ~󕥚: CC;RPwWW߶廂[Jk}_ɾ-{hƛe̽ƻE:hŦ袍h0r;Y!Ӈdu. :d r)a[SikPIOdy|'20غRMե7V}!Wʵ7y_~&ˢӚӟGk)k^mm)9ZRɭ˪61rݓ ,rw"Ȧ.A/V=l͜6m1*۲hwJ9{_¨h]Ԫ>ǿ5g>Ju ,+ej^,M]/ 2r'ŗVn^'۪w"_Gwmkߦ2jo*b0]rI_ԾjtԢ)#Ȣi-jߏ_神%O[~߭5-ޏCȗ%ȥ=1u\~h[[U.f6%]M,NMtܦöu0VYjR}ݫ.9l?-sw$\j;/{}?|zlc5s>Ӄ@_IW0i^Kj;WVyLm3n[>TUqqsj<;͗婈כnc]u5rW֚J\+*ylPC_}ȨGůcڣnWm;-ȩF # ZQL;͟kOtrѸ^þ{fz1GWzyE%OM{\K;r}f8h8U-wu_׏SÛML]҉]IE:Uѷ}kN{G.7(?q j^[o{޺r\_P_r6S3[tG~|H>+qo_=Ʒrq=k}G ۢ>et=W~wgs\bI>>o+g=R_ߦ=@k l:[zs(BA>ONq>|^ǻJ.'Iq]fP/,z]*_Ca#j\[xoCɕ0ETI5NPmk~O?tRSEQ]کr>%ҭߍ2/8٘ԏ䯞>]zgTYOׅ|1%*~8nډwqdkԻK=>q}<Aۚ:JS=:Ec7d15fwxI_yK&\9 Eۢ"c.>4u nұEGpFiǹj-|* ]^oL?ݷi<Lq39,QWs{hnȧC˚'Ғ]î- #=辗趢F3z;.3%y=tW-4uJʦ YԻ~)ڿT|=%o mt]*ёٺO&s1S?/Ոܛ;2TYcCM]o wr5sL"[MnE( a)zIM]hP5r4zd޻Ə4uCjАNG,?YxK_1xc]TvA4WdnWw"UFGQIgSi_q>Xq\ds 3[@f;;CŬMnY+M5&דoEe<1I{}՛=_9.ޞJeʼnw-9uǟW$T_eLAׯ2>,wK_v=k 3 tBCuǚ"v|t^{YAngݫ۵_owyKM%;+Z];~B:5X8սoVҶJJx69E+mvHviF3~TaԼ-u3qQK*a|j@5uv͌g7ge\=mnxغt5)W"מtt.`}ǃۆZXō^Ҳt\UZ]jx[ڐ\T`Jj4зHvxڽYb7NM?[M]hm>A 6wPLVuQ_ Tb`ژ?Y!x7'"W?_19Ju쇎-I5I+Lw]kL]}ub_c|Le2쥝 W*nj~mn5Ƕ][vWȶjų} ;lS0 W\|o[FK'UkٱVO[y@qIX oc̗(fW۸~]q5QۗUF6E+5_6;5 VlF'۶sذOTO^zDu3G03]x@2ݳq_&P|l"6>o7 3bw+U~4i?O}^Twq,RQ.ͿmsBQ_V݋m (S'w-3t:M=q0Q7̸1ok?-_Ty HI5hk5:KT!eE\L [Cy}q]9}7U,Y,VyUà1nL7Tee\,ks{:o._zǣ԰䶩 {ۤk/oKuq^񕻕Ҷ;<,&ٽ*5ܝYxuVQ8^=5nf^"{S KF]PsZSW]Wא:TDLU;ѥ"N )+l ~ٴFc~AK\RN^~,Rƻ^Iloyv`k+ #oQKgVUJvvw:g[xc`W5|jqUO#>oAVNNQpjKm]@K5n7ƥꎘ\2=o@d31uESςB( ~\ U͛ۇǕԸG>Pmޝ43lG&):$K]O>]J:unXèi?M-Kӳ'g~*;1{]wsjg$r:/z q2hn6b擿LxY?ڕ+UL=YږոYQˇ^N Ƒ*.=FEJk+g0{B#)UggRuckznmѯʒ+jU^=[63Gad?{OwExjB1)OfPj<6Ċ]8$u?S +ߪs-fx٧ƽkuٴ[u dܛv9\;I=&noǏ/1Cٽ W_v{}jq~R#6O_#Ws+Ѱ0 xŗ?)Zv˳=SL:S}#+ݎa,Zi,VqWmtw#1֎=ۿ\Gns7}~ƣ*zB~M/NMv79\SgV(~@ڢCÓVQYUe䐼xƳwLO 0 &Nڎq{O{=5W4[w7 Xlc5Ʒ/8T%ݏ_y~r*֍VѽX^v\ nŕ-&n[9O~LX'Pd 4#Ƀ^/=fQ0YOh)ԴyezgCχ:Բ2r8m88~\ @(C5>5OSWc?@ݛ eSkj,[[Ml7.G&=l饩)l60֐p';V=⭩kʼc^fW|fܜڿX S qa?l>v:n~I!Ym]߂l6F;?}'>OOzSUjbOpnޒJ (d^͔᥼n|8r~p7B) uz.JIA޽5sN̽nI΋SZ[E#owԹ&+h*ŵ~Y{v#>JQv 4uZPX-M~Zs[wkYse;iT&o&?)/U|>|oԕ3ޙ5l|i:wÞ7VLvZ{6,-{ݫv^ioL(hCՎz h,7C8'9}{M{5)᤹;͒5&i,o ݈ׯ?T51!TSfT1CȯoD6Zo*o.0*J5WnJai;7}ԎKIoZ@QjˈiBg'DacX ]GPDaN%7{lpXgr_6V*l-Y]MEUܤi=کy2A뼎:Qx ;WyczΝrvU:cj}։Oxx:xuI5'0p~+C}5o;jvQF%gӚQYéq:[~߻/mO5KÕwk˩pr=⇫q fUmD ::`+WA}{POǃ~֍7ͣQ{<%E'VEr<=Fw~x-֍zqm4`mhאԼ,ڗ6lq2E+Gk5N)F<0fToQxYU{cLݡܟ~fReu5*a~j<nf,j]%t7V)ZV>Y^d۫[;'(gj '7G^u趔aϐ[n>{_0:{X)j>g6:\g%EѼJ֘?%Өot{ /CȊ|QYj7\7õL[̻;LglthVͶ8^T@:ktXj~boԸ)Rc1IV:e5=OmT?HMPG&yU$O'zg=Ghd*qpatAuڳV}u?^dcE'yTɞUu&Z4{MFq=={ w]lH[Ƭ=eg*ӺL\7NW|~T漡Nk?*RRk)d V;W8z絪]6š.kUg*?(p{R|;ޡ*݅~ZR&Z[m/Sa&VV.{;ĵ*8ܨ<+'1j9wǍ- uojC͛{3·KYKuM_?i[gz^F0W}o[et=!i_f{%q{:6S:O!+WŹTCsvE&T@uQ]$2Mɯ۫gw/U^t:쎌"wNF͓iiy;qh0zҵIΓ9wuޤ C)ظSlȸ?[>rשGF;uܯ=EFSZVd7%yOdǑ]ciNոa몋-I7[F]6[J<J^:)7ʍ,3 4ٸ7cעsq5M׃[vi܃;Gwp+B=į?wZu,5upJITE]+F: ԧ1~-ZtCN[w3_vwΥuàERɤ&|7ԕzc^seZ|fkXy}rm}(ngw4BKXYP guL&y<,ߢu͚:˦s~Ӄn)[gRi)]n*Spڍ:jYy{n7r5^dBf̫kꌢzU̝,|`Eofyݙw~y{nˢ΃wmNV|+n1OSgE鸶FJjj`SCSt'ğNK>wYWޱڢq:B&89P,?-iK ѼWɺWftM}fӃGȯ!4 7/&֜wΖ:~ոs zڷy)}°*?niRApuZZ}aaڡ/4>DKYnW/jJE6P;Mk*=+UELoz|];m#{Mik{:p8E'w{r+[.gM]ّ?,ZSGM[N_],|webKIkɶhUGjLJ)6o:v͢*V58TBM]J.CƼUXڅPcM]m"׶FsOJu<T;bȘۇS}\zb@c]\Z˶Slɺs䶣Q7r4u~mM^olE1'Y&\_GqGM#濼DnЈw<ȻqOinaVI;3i!N~;|B9Lu^0Բ‚BjW+Ξ4vIW`U6MqRjysGP I|uK]H Q8;ylc[_ao~3?o#TR;׳zJzEߩq=/x~N\H_khLP*Ŷoj]OևlRTW^{LvG?|vEu/oLG?T"5>L =+DOVN*A7Ky%"㻺uS:?\D'5syڸS5O?r|J1r[.$[qb_LADPđ Ws?U|ԦݨI/%rKzF+P'-Yj:4懺n8z{@Yd^3pt~;T-G]e?wNnu/щ}sEY`twTNTz˽jJ:vdϯLw^D[fq qZ-.j9h5Ku%)6B[j)tR]7dGYr p5m;`܋?_wf[/~½k^nZQJeagajϽɏ j\Kˎ,GnL=IVxSȓd%rIvquoC?zGzG-6mPUn9\%wp9w-G=^;< uJ\ྭzҶ.GyNn',Kl7SGU^T7 lz0Pl!_]sЎyٻeHW=cQSr Xcq/ҞͶS`PM {$Kܖԙz50oE+toǎ -HJĝ?y5TuyuמOMމ_jj֛ }W2V#4)hl}ZoNnaKRbj^c)󡐤1B?S6/8s(t[]?uO6T4Y*sjhN϶6_GOW\{쥚wdZsظ@unc[fb | 5?ϼqUcU©H֠j";K-WȻIxs{h' 1#7 iAݗw/Խ~}!96_^&Y}:<7L_J}DE?pEhZjܧaoݪ}e; O[gxf~hnPgʼVEtN:'u譅jmvۮo^L]W|ZKoPqØI<9ۣ3l9{_ OijeTw}RZ 5ZقZxd;Ƒנԫ1WɡLexmo׫xb3nk@eOMK;)<%[,guZ*_t4>WUn/X&QиxK@\a:ifm:a]lԼ@^UQV?t-ׇN7/WW 6sP#̜Wǃ_է4_/l %7nS'EۚFNjfmU?,OuiCiە=*QP5d1J{ u\дdg+J7*yƍݜkꌮۜO_B~{Z1#X=oy~&T97:G='1R{OfG柩Ak2kj_ÊeoƟT:Y^S^7_=*I#fb>U$_5!ӫj]ߧf^X q~|ק k3{ƵE1;wWm=fd%ʞuwgKmIUr dc8<~To[Sgè3<49c8v3,⅋R=!?^k|>~_zϳ߶έ2k|s=E1Psj8{e6PhД93}8Vn!)7Rz @6ݶnsVYS SbpI[=L߰":B5]Ceuo}d I:I[BI1Ue{SɔT}۩摱)nN᤭qjckU'4zݸeNksZ꾠NC to9ȷ!=u<^/gy&#W.K}Us}aX=8[x']Ϳ}j^߱Rk jP#q)WlU뎹ů؟9 c>\̓p]iG㵄\C hs5X/_^v2:yavE,ZCծp#G\ivk5p}^o_CqBͫ*9ȕJQGȧ:"֭nc!gr⋄鯋}ᳮ길5ACҿ'م*VOWgKoUp+A'=0Q]_\ӶaQz_@&Z?o⢪U%&Whpe˨:Twħ%ÎWӲUkq%s|褚Ut!yVHc=Rf]9p?^5DENYvuᰚXN` ]T/8oG-p4VvI)߫`2ztBfwou2)"(j>^j'

+bhlUҶjU~rjPbrʍ]nz~:|x҂']w0U{Á}%aF5(Z=qrr3U>>ڹ~M }XPr0~Ԭ.MAjL+fmv 6L;^g _;.w*?)pֽ7 Ɔoh[}}cr7#WC|4}=yԄ r>Ww2݇o\ou{t:EOuunT<_3?L0\ۭj?Vqӽwh.LuI&i25gad~bDtݗ݇~}{:2A+űEd^}{&/eNPW{ ?<+Ƅ>u=m頣)'ΓoN2'wm=nzUƒT*|I#)TOVv}UR??E{K݈^zgT YhgwΖl Y|Pmo#Fζ{Q⛭1,x|3?ENf;]ϝ?F^KLlև\Ng42]>$vPuNMSHu#µ󖼟W>0u ~?"5uХ;.DPUEPx.ɇt=~ѮRgcQG{9'^Mد ش,УHY.<*eL_^oz[vHwRCjjl6T>ihT4K1Z ~~@co߮pSΡ鉝#rZQ=IH̪rJ&UNs-줩s߲}rڷ)c(.V*8'gt$ۗm:Z@?ؙ.t#!^:5q0vy}֏ye7U_XoH_Js~s-TgqsY ]5e65-s+UOTᡃQUzllSovE /LkMMFvo)z-ԮtZČ˅oDQ6 >Hx4ҦOx^:g"M:t.Pbz\K9 j\G_߸IǩZƂ &u[Ȁ߻9Wޡ{)╱>rb O Rr[US/z?ҶmSw9qKԟޞHAgE͟G0_lGQş/zLXVR}deaI3ۃg12/L/MzʡY_Srd~Yu_Ono_997do"zmׯo{Mg66ޥ-~A `uJ_WP^?:V{0auWnvU@s3ܧTeIG%6^%?Tck:9A]eYH^[lЯi C+~:~bie-{Qjj]2EOX[tI1z~GnhoVKK것ϭ[_?VS&Trno]^*MڬW}$iziKɡ# x{ΩysU8݋޶צo[Mzv u 8{~!W𝣎7g6SSdØϸON~?P0y;J3ʘg5~-qcO)4dYZ48a"^:.q-Ưc&%/X7OƲU|a9WbRƼj=k[%>P˭ոQȞydLpN {vxR6w0ZWTs8PbDq;ϓьeVȼT#QyDsi!vI5uTP&|Ví`9t6u_Yx8^ogZm.N%r*9u줟F SO[e}r{:2?g">SU9o6=YlWndqh]7y1N:aֶד߭HvQ3$魣zgj*=7}MȟO{vMiyb bE8̱ۋޚMt󗟸֋+'jH%YONoYl0Ew7t"* ("҈"("tH؟^~<ZǚbnffMsO Z-խtk&@4ʺW7! `yz6SEqRϹ,%YzxZ=ڲ&e6x=z]W MTjl:N%_@?k`)ЗqˇLxu.1eD$H4ء" J:qvy ;-t[^0?;Wfû[iP{.pG\punW ށUē. P c!ׯS9)z/uÇO ;P>=_ tċu 6bEZ(W'` )I@D)0;Pۅ\QO^BS@>WX )/k{m ҕLᲈg.WOˁ\CP1`Y?2cM̈Q R7|VkVqJ?mmmwZMm!Fώ]/;{UNaz9_Y:,j7Ai^(Ø::d5Dwˀp̏u?*xΎsCx#]OL,٧ꣲy(nR_p|TJYlȚ`χJ蛿 Hx@NcNK .4 3UJsc ݗXP8ruLm@3*+1‚K}V>8>yjYVм|<#DF\ utM򏍀䔸L0ASH%Չ1`kS Ay-^.jz51t??|, ?]þ!yH4br4>0GRQ~#ÚM |06Fe/{HpC/J{0l[zd7wKdJ!K ?Rl3d8ʞ0%[ J8t x/G=ݽB"T @pr>~ؒw@--a;wn}u$7PLOo(O(mBq=Mt#)HCC$m9joNu鶸ONqyurP1 ? ?&H#|C6Zm˅E﬐){"w(I[')m`5q|A>k̢1Q]|8UB8 N0k&<9$+ Pނ(0Zw݇<|-O"^ #ř]Ho ?3{H_˴>ӎo#1D]݌@j67Eݘ?}aFyV5 Zn(z-fCA=TpN@(M%*hȥq Pi/Ѣf}ARh#T:ZB/v|%@Fp緼VO,όxLZQ龈l 96/5''v#mo(@ݽGCsL8A-#sA|b Eu4\?_̅=a(oL3RH iUbJ^ě e}mݿkF2lbzLcK֘F4lfT~  %G q&*2׭G T$s'XЭкfן2QIGخ98]֐IhGYe煽gB@X0Uji?~f qǗ 6 y]+ fH: ;^%:IJSAvn[(V?>ЅoH~g;A6ʹ0NII%3Tl=z@X9\º["21dM,NA~`6vdX~~laN(U~H۞{h#S2Át;ŊΑk׋CJ.^gH07d =ޙLqX{~.a.N;J50dG}$Ųmwi<%¼ WGY)8RTbqWeLsKgqx¸Tq&N|](=]]T7R:X8ap6O-qD|PMvJR'7.+ }!^*90 ⡺߲ъsx{e嘒 JtR%'Ճ5|Zzo&>\L5KV1DY|wfߵ@L&wP P n85tXccMְ'w?R/H?)] yo; D"D@GXzp)"]|s'zeU|mH|Ҿ?b0;OZ.&c N\) d~U`3BPds1s"l=͗(NaV-pm6sgW7Yo㠪>?~ip" $n\@[EnwA؆#_$n;y7̂ |(EC 2ϟC8Y㐘*:M1_?dfY ?TDohF<~H/wt?Q#'ߋ^wLw5'}[p[uo3)6"\OG$ ͌؜ z;*"~1म7ڂj?AL"}y0Z7?pV2] pdduk=_mq/oC\!Q |/ 凲^D&WY?0ix_͹sڀpG1yCV3J t}:l=ϙ3l *ro8E',T < ꇲv!SS)Vo1:i9P #D_zȰyIJ6/( 游/vg h!=7u?|Ǥ_Y䱋-83_ڧ3l{nQ3[S;pd! 5+i0$9y"\e_؉'f{lddK ]s83,TQwv;u#\`gfufٰ"9}ڦ[őw_Ǔ9n4U\p=|"X%*͸_Tw4\G'd/o&ا+S FxkfPAAlv>g]bjhY3rG_ ż>DGɸ_14"kTădY,Ԃ7_E j;?a:8lwTbA,agֿⱹ0oOC#=gTNk\;|K", A6=C ̰ \nrZn.>xDH- 82wU> љh,ڈW'GFYAyȢ$z|%z}fSry"~N{LQb#v}8gJkSQRBx:s[EA_ {z9x9Ej bȀ2ɰ㌝ `uۀY=3jXP_?G@%n  prR .\;[eP<^uX\n1Hv@A3~n.)W'^08V_tCs:`u&UUR$D踬3GAWĻc?CGW>ۜb% Ȑԅ?^׻|@l@dsF/:ڴ7{)ʷ`{P> >^>lY&MA #FAZz4[x47bќG 5eh/+mg晩A)x\WY*C-(yb!W9/~@Ix2pzau7W>I5ppI<)LՉo%{ ;X~}!-Ux> Yl.M9+rY1>>S '㒢A;C>Go8#B.7:,X?5,uw4H:L1)F_,5w|_\>&!~Ł>Q+]iPxGYY 5M:Oӫ΁Maɦ1hyՉ5I?GjŠ&7'u odf.'o _eX_*h}jy,J#?+_Nr=[^!g:mwѨh+z E98 4Tq8&b绣抣D,pl4jAIjE<Ȕd 7\ՑQѭœxlA:+eA;3'1xn ܶ״#qm/(]u[g"9UD$٥u0~D,zj?yȷ>@&quBX_4w] Y0-^4;y~ѾpH $^ hp'ɚ#HmZ`.CПcҮl1X#$%@;.$:0Kֱm2kB6`oh%l=B{b?ṷ߉}aeӊdKcsJ+5a陝R?Jlvr=}\1'MN}D`^|!N  k+'\cmE_N$|>+Z@?g4o߯Ke^$F1a=f4qPs낤 *Iᮑ|f 3lvD =8gRPcKTv!4F8k8if c{?2xX#(3Cw!󸥲ϪB~Ǻoyf { )mz?jn;78\~|5ȇ_}oT_;o}i#[-;z⵲?7bhm͞E|5=߶Z1G֒̌ $3^!}m^r;,ߊ _sR\9 $YIeOк?ځ1S5@ SK?ɳ)g6?, <"q9QI˰?\FC3/4RUI6t.^`D8C,bV!|=:_S>s# wֹ%uHPxH~aIQd}ae4> x^ϼ^b#lV~ɍCPuVlUOE,zQ-u昌!(T9|t! l #TٱG"rKa xKu(;z;wЗ'Xt~ޜD+Uw$u0uLY>A7LOW|0"R*λ]p}e1HWbĶyt2y4@5ٷS;5ΰ11:}TgdZu$Y+Ʒlb+_Kɓ_^5zQ!D;:ApV UI5g({{k ;l>h+gj iz3kx^ݗnԮ1E[6~4o]IX΃mI kRyUtM:Vm #-ˡ(.R2>#āN*ؠ_-BԱB{ *X`<wVeAP#M5.;8?^Ԕ7 ڐ! BLT<}3oA@T,J;py̹,5m>KwAzM=95Pj}WǿPBo{u(<i[`ةP-197~ƿGX5DG΀;!M>#<4WGLԅ$tt v:zW_)P[ 8y 'uZA!sVg P}Ȋ jOezzK3/99X~}Sxm WLR>-\Pcf.zjt 0ӵc~3辔"cL~b+3o\Equ,#@y>?}\h땰 ۸MJ{_uB GLkrS4^pfun2ɘYTjiӾ ž:U_] hqۨī둲{ Ԃ`oLb'l'hL(yy]J~zpt'@%v^h9OU,ynGfafx'?4>>xG*7DG}G@a~,+Ϛ~| Ikz]+]O"SķE,/F|Jް?qX0w?t%O28ˠJi?%^f!З+@ۋBOEnE^Pg$rxR;r(32\g'?։^t|޳ۜrO sbX҅[7>^~nm +E;`XI}9i] =s!=bs᜻tK?Ba^FQ(ޘKu1/ի|ӷyx$5[3#wY(ACX <=t"[7LZ QNHdPwڴ*J]B|`1b'- :F9G zqMaj컑}A+ثMއ\p]bٯ͚YHm߽z}M9>k]ry xŐ2I[U5B<_č@HYr 't74{ y#syzGlwZIϫr$-~7?r8x6nEژ_&uqiʉ -Fó t<_a:v&?EyZ2bHNaY֎.1ykZ )bf \ y{g\$v] v?j4b7[+ ~ʗ9tc j/&</n~>IQ>-a>N7"(@'$ ٷC#5 (ʯe:UϝT/:A,4ĊMq9mVsfIT*n3kٶYxM崚kцK]7e\$B5زփm&X;׃hJa\¥ (-T%ʃtꘕ;-b~gI StYg>L+@!' /3.mͿ|F(rTV6 |Ν)0P&z@~^p֛ur#XpzP!7({ɼWǖn,Hd 4$:A®aPʞhBA::*_[`dn]k8Ԁ';Z|xO<8 ťK;? 9#8~eEn]v>hsj?漚a3`5?+}W`Vw\Ga";UoYf0>Ϙ^1|v/LH\SpkиĿAOu)jkc9| ϓUa\$n:8_0ȹ5駿sͻSxݱAIB:P_y/$+ʹ*)`\A*++l߸ V^%S}#XNd'nZyg .ɽjmIɡ): ~!NIǒ1.\g|0X| Gf 3ۄ:NsPV\xr+G"Y#GB@2M!6QFwCѿT ;y*B[D ȩȹL~ӱ;H@3;|nHmRm>Z *7yRm,w\# OEAfz(ezp'|a*wyywE.]=Gj qX}`I]Bk7 ~|MOQ=4Ns8$.p-iC2ǂH/F&cԥHE^ |z<g{2AeL4%1x]n`*c=H`v[!{>ֈ׶9k36qks֖NX_[ *xuiɈ(B]S ^3?g_UHFKKz _xX7 l@O$M."Z`j|BAgT?'V?Ep [ـS ;:E | >a#J/-jp YׂQ+GTTFr.~1uvuZtyDy(so8m!AZtʋQJ fG\PRǒ(gTO`;6r,ҙ"iGkVo7׃}%Y~玮ta3r9wQ.6toltKf>oUs<z׮ckTʑNS{ۉsz,ʿwnoE:|Q¶*FR):-稫=.-$gհc #?cq=gpe೿z`;3Js :5g#̗o70⚅3bֲVZߗ9dK {&ZgX-*AWyBGHd0wӢG@8~[B0l`kQ=IqTOѡwc 1xsC8\ LT"QWTXVHV$\: ;Gs}Q/~m6XLH_ Q8_lI:؁@s>ycMP)nrԾT}#'ڦ@̪ #M?y[Hw1-cM;pc]JB/t렼4$3jLO8䯱uP]6ʿ;AExT|ˢ8??Z@!P)lvm#Rc󁀵&ږRG:f5&3؍D}?GYw6 ^c h0/ij3lt:+Hqٜ5Չާr'G}hONPܩgdAJ1+^)xg_ʺ-4,&HlN`љ?DԾ ͺS?yB2Ѡ+~~' MꄛȳoEb@ir3_h̾Z 0 ֖ DQxY?HqnHM⼨5arY w<2>D.39{'k?긿PC#.@ӹ-"ɫ&AkDgiV+48{ Z+FoX-楂_}-#ҠKp)Dm+2ޤ8`+ K_&}Y3qu-\|}貽y"-݁E͵q9Ũ~iD.]`Xi?v[}D;ێAKW'U;0].IR2> b_5HoE}>v'+Ecf@)!xzG[3!Lf'{IX`[rrI@0H FF+hߩ5 k[,A Īe[ҎJ R#v\h~CdNk"KŁ in\&҇6Nխ鈴AjWer |5O*L1'4|y}b-R|8 PF`X۷3ԑrIbW"^C*-E-*:_4r\!/O7Āy5#WAoiMގGZT& y.e@ZǞ z/kD'c5`DKȏ51V%DG;;us9 };A% '8QyF4'o*ix{HM^3З~yHފg#7C}*VS!+UV%{~q|) _C5e{ ' }H?cgJV9y(MenǼRWGG`!m:@ i]usec͜<\>e 0D!>w% pzp<ݷr, 0e/2:SD"heq7p cWfC~x7ס]SftsWz=uOAzZA̧o^Oh9 z>L$!mf# r>:Nx@b0p\!MaJv%ebS0Y *P>S ͽQ7agi&=^Iv^U .a^*zHOqY8:^3IؕX ` 5*I]#-6; ?CŊ*_˰0s6(u$5O0mnom P;) ڂ?0״F'ybߍޛ6asIǨw2V}c+'{ S@oxD h(x9cȗ.4uG"=UP_J G({:rlG,ix\agNx&z(|Jܱ T"Z?c'cZ+=v&[bہ!ʭkAL?e}g둪L}j A sBG,W_I*Sw͂D6ӧ:uk7 Zx9Jϵg|/FՎ?@hE5o|'yaϥ{ w(yUܲ[- jqj% ?O\uD>%tÄr'hKm<c M^uJjwQQzrAhApovFY/h JJEEMox1=)`|iS A\;(2|6\⠟,[*| ?dVJEG'~>UV:֣`b\puԍCE 6/R;حw:T\'g*Q =|޿6>AI+׻ ;,]CYz:ΑZznPxchrD/9[}d({b>⩾xCtf:?>uwGӪ@v`_( _.9o<.5wua'wn4y# } ve~B|ChľN >+y̸ʎMĿ{(PL F7{P.]U C`1w:_TIrQ_Ƚ?}BiΛDgyh4ߖ%PΑa "7$*o#?qx v* RaҠG?c_pex׼!pNI3:Gx }{ egc.w]v_}&F*ݒLddAϷdM!ҫfD?r-@חK[98P6ݍ'$k:cħTk>.?x"] FS"MO05CX USjPn_A}!-۔qmut}σ_xt_wT_jD"\NjLP=BD?OSMVlG/ni].Z] zY,yYxݬE9W'ʿa| T~=j{ X52],B:<9Xt&پb*i ',Ws/ Hr utm>`5p-U}H+ꈝZeVeow1 \rExD\ile5yyOqW)Y֤4U`@8!󧂔zvcwim,qiU#O7B/$( G2cs!%eWFJy4.ek!kޯAH=t?iyU2CIlٻ)]6[o2;E} -^@wr+>rD]x6#:ϯG`ly^@05) #\4f.sIn/[{Xv Xs p]ߍqS_laQB-jONuU a/rGKPTa<LJ99qP>ҩ,V ?Guﭸ OfDģ3J<]Dty*%w݉J}ױ;?9)P:-)5~e:;/:lAHkek/\zπ\UpN^;و_g$%{f,mb=o{9yFV9J/Ш3H뾬*+e e{޷j| %{]ԉ/izj}9;d |R#X3N:hr(|,?y,uؕX 2>|%ѳǁ䤂:ttWWY=Gjs=9B5)J Z p.lW}PhKd_:/X!-&GRRΚH)ٌ=x|eJ=䁔yBqura_y?l7jm !p%$9U~K|?PSH? QK=quLq Ax9. Fypl{S&}l)}H= Ҏ]ĘW=&KMCAxЧ0uF>] S*>T>6j@Y/Թ ߣyYF?Wkxg@?& XAw#IOp]'tNO y$[_!X)v{P/Cd8qo닔`0 (@֕)MHFf~[$sK_|ȨK-{_:*-U к%wU*hҲ",/_`9t!}1E҉rz5wdVLz_ 6' z9Ch3:QQ/a^oSy.ɈO#74DY‹YϏͪ ^è^5GlÇOo ]21u0iy^UR~NlI>+(/ӎJe7_Cy447}C^N%X' KvtIuR?ct#YD6iPNIA+4fuݥ3#E[K(ytX=Hd{!D%#.;O*ēal:n\7[.:cM#V@VEp{iMhJ _{M5- d%[IHFl!HPmO!gR=D2,qY> d5Ir';[ +CAwo&{UAeo2¹LJ" NcKLKN}Ie J?*o..p@,88>&]/o ܑ? mŕ ' AE}W7z@7߬}12FWi?sݦABKis)P,,ׇe=B@f& 0M\?".~hssKD偤 ͚yN^bI0IuZy[Ƕ/!_na#@:";^lK]Ml_h{_r'w$Ի#Ae@e_IlHS{lhD+d϶ aK5s_&6Q}c@5}(^B#̚w: lY{8(\p4=0_iڃ<-{~|A 4kؔЙ0Hbl_ձ8y} 7Y.ۿ/]te_~'yr$?}\_u"# DG-tx~iOb 2z{?Eq@laԏ;O>ձ~ #*}y/sF:f㮚w{?Za`ltDI db|"v.u=nSnLd#Lys'871q'm=: quJ-:;N_6:zx@ۀꆠ_0\(=e A`!ݸv y=Vφy [fHtՃV=xJ Hpz~>(uvG&7ޥ?Oo6v+z.#E&d4Bl-Qh>,Φuao~1Vg\&hRȾ@9R:sT 6nYǒ ϼ4Yٲ>)B8ڋAC?_;ᬿWݶEgޮzD)F- D{&n"$W';Ư Dߙ#WS]&Mk3Ľ_K;'|~S !gIf-PqqfB{֑Мhcz1oHeꉨVq|zgM?ٍrZ@H9!V阵k~5q9[¼\`n?zy}~!6 r.=qZ%gݐA3 '% VexHurC4+S |soڣ*'^2^ 0)GXEmy"]y?L">xvWuv'f~qOX#~DET$#`.r"ޗBF.lNS=k]Ę{샲鑡6v"}G.(;2+2+3 W}HǬ$]jH?̼#)f<:l3$A3۸VLF]Kdɿ ?CgP-Y{}:v6*qDԛ]SMi~ću.ZQhs/@-%ꬼ#Z!:܋mwW9½9-~C<<7~"$] }AqzJ3A|yME ^E.=9?zpNE : |i#I XiepOW_g ]xJ߉? 4{5zQ&u=PfaMi ^Ax?g1jL0y?0aXUm Yͷ?\MzN#n8li4Tv2f`ĊS뇱h)a#%F\f؛@Ulx.?__|zp ~Oʥ];^z]luMn 42珫foUfvC'E,6T?ԅ"/iYqu_dk+o?>UO@U I*1 LԒbwa#I]$2~? -rE^;}_- =y [ޅR'<6 ٳd4:A..L?B1%c86J,@ #2@~wOᯧx"'@6.\qܜ,quNhuMTiDa`h;d KAq <3]ʈ+r8񗧕<;/Z ѧ;'Jq˃,`lt}[ܬ/0upf0JY 5*{&3nqj!3͈>y2Za/k}=>ϡḩgn1߈Mŗg| 'cA_r1KIOp#-Z\?n'}5:3PǤ.~C{)p#Ic$Z)߮ KاɈ2wƋɗ澳#}xh>+ cS<`6{ o<$J`'4ʃ½X4vA:l_>F k?Dp:ҍ}4e1Ay D;ׁS[e$%Be>0g8\40xLs'#}Ŋg=) .PRv tY#EHڧtUV[gƥa-+P]_)%GZGTG_e"vq /4~Ի1;Apc^+8Q~v^lEh'hE@K1=:>:2)1zπg~^t&ↃuhDBJ:Jcn'}vzQ)Wq=ge*yͦԫ@fĞTa@|[@ սtxC(DB(nft-{&s{lwS \=b{ŀ:s¿;5|icb9$6W@FjHӎǛXZ%z1@sL;|pXi"~5ǤsUu|dJ} l)>ߌ_^h6 %JݟS" @ ϴcw%9?ʝ́~CZ\@{>cۓ%J:`]wT{w27,|oETO~)8~0G9d%a@TьϹcY}>P=sr^&ޚQZZ|kF3ļOnr|_+"y&iD%9 &7(sY֪~~|#`E*m@W{,g~۠};|%"؈ȷAu!/-t|ʜDlBXG8>ҥi<2xI(Xm$V0O]8SsWO 3aWj,>8qd:F%}+.8i ݝb0/pT/L!tԠ^wrelj?Pdr%IzOOË=$hԯ5r7`Rmr;xF3+a=YWɴ5|r\cb*`{ &( t[m8͎tǣ6}uau8jMy箇91^4 ܢ4Ua|q*mնj|[|8n-k+KڽLj|?lQ2Cڷ}f%E=~ih-\=i^M ȦQނ t}Q/#SspO%zHZD?] Gg[.aّߡĩw_x|oIG\g/G>qY%%"2g'bK;3`^`ݶE{G~#5u#wj m-ɲ!Y0]f(S Pvݖ̏Fl4K:s;Bʗ7w\niF#VH {J!T)wc }ޱNs9KˈڷDH=b4xs܁iG ɣLU&(*{rqHӾ?\w0!=|VV`}\rܾ"F.+0 ;{էw(O G-rv6Hk@~ `|R|Ü sDXզ];'>^.jQP/y"i2s$Z&[N=r nUET. G@7v(nJߥSJ-~?n! nc'k9L6'/#>\HbבI -iB@eha [ Ek7j z3Fo*;/13b5akͳx "rDz R2_ 3K6q9k_GrZ!ވf:L]R böB㏃ɢe6Ljn'~%+z3Sիӂ 1L_w<05q<4$ , Z%Ut>P I;D(AFpkf?`v,`9 ]*vd\^wĔeGH-ްL|v(shgAD|9S!>p /XzpĬ? d.=[zH]س %:CU l't N j<I0 ~?E-]Ʒ@(09Ilލ 3V_~+KGLpu0>&eI,Zt]*~{Z<)0̼, gG&U_uueLel}uM[+(^2HɷoϳB/_<`|nOz (==./t "`zf-%zA':[;NIjj]GffL 4uluݠteS4>͆V28ˑo^v?ݙ* M/ Yb㽯Mk=A#uK\ߨߝ7=p{d mDف}5F`"4ew=q`ߑZ{XԻ!*EDuMhPF>}]~| ] />P=9(,mYSuN񹍃{r(B5>ؙ0:`O0MwHYEgKL'r.twKAY@L'7$h)~|i:'V>\L >Ox/RZokʁOu%Q-.%TǓKU^+`vA>zrH +JBW:+ߗC[Aej.MM`ڪ٪#/KgTlA^@(e{!tqMP(QB@g}$ VÎb9>"">CuJvyvr{S<d ʼEQ/)ysio&[jz\p.dC2יobonnp龷:uP lnReHOSpsUg~;_| `<򁲘q\춣[kHG0 Yonj*ga7=} @/O^%DxoqYgDu9_?zE3R9Ωi쵠w [kb^mlSjj _J_;A.C˭V9y) ښ1GyPM5>4oĂ-[FjQ-UY(aPLHkoyFn8G|v-عMX+b ~i=0E;iOW.*}|]u{2wz3c:Va? .+vH1N x Aڸ:1Lj5 Y^t;0uD JA~7黊 ~ƣgKO <^?+x랥Yv#`؟>',@)|iu3pX(>JղܜTA)GNEx|{x=Oۊ6Sx9k/B٢On`1na_7hP+ 8i2_x_"SûM@_;FSqu@vhWN=,p'W mč Xy=>q{n\QVWIJ#g'Zeh-uoGbr?.ڧ<\7q P֞W[ NjK-, qW'Á:Ǟ6gqulTgAAAP9`;/^?Z=q;^ގ !]UƯĴX:>&9|n捻\# Avo!k:[s7mىê pN4Pخ.>MkpIw~7>$7w'%ʃ YNbS>#gNM0bAe3HW2A%Fh}0ow|f?($uˮC-bW9YS șe<_r|tIqҍ \y`w~ST;ˠXToy25p|<JGt=@W#[\z,+1|h\Wu^žcsge A.)tbZE #BgYalo86_As̺#(ܾ; M'I}D'>$?~k1b);@B8lz(Vv{("贰?ƅ@l Tx? H3.,,2XI`=vwAV=CO=!O`[ƫ Vx3Lt{<eaaR{Qǃ߮FrnBT|͇)UX"n5(^%COPV)Gɷ~#BM=d٧*߯;ҡ-x'Fei9AERigYxYޢ*-bNG?ȓo$c!ƋHPI`mG)"rNnTG*HTZ潣N):/n/D``p*Pn`:| !>hmuAAP?] ׿ @t $^告z>t*tSlck ZhyHH[wΡW=G0lbcB޽kGTLþk/`S75C{EKAa麾%Gx&{,v!dI>@ޛD4WugL!/ï֗<&v 4^z#<1j-vs֩0†jmɇ6'sӷ.wpMe >B5h8q}GN J ˥%r e4߬Cfs;C)f(Ap 9JmLH#_4//ȱ[*G Vx\U,@|xq'Ao-<%p`ͮ!@g9˄ 5J~vN2oY= X^E\RßW fcqA;պoh\T)E=gOHl@̑~;"nMehS/ul%nI,6@Z MPV!݉U伊8NMƷ\7}tJ/@#xwn4b+ H_(gvVԹzs/ 6<$ b1&/^t=jOfܩ'G@FD;F.\( г9@,7(HS)j@oǕ&lUU(ƶ} Hu^M:fei$ȉ}aZTy%(5,x'2~u7J@L҇@y~k}B(r˞G]-X M<}/H4^&xX}Y zmlPZse<^Ǽ7Vi>pYCz:vg+ɳ !Y(MtJO0Q/` 6t4%  䥺n:~j P4Q9 lyIJ͸:󘆺A{ee/3z%E@t$xcg:++(o޾8|[;ǟّ|\TNG Wƛ'|xVtzY ե4׿;S!ed(&Kc~݁GA>in`H"_-\*돓c[+±dI7wyc37F))BpNXFt?rdqTçB#J%DJJn18hB;BOm5Q>HK%{c;tRω|GMkXgs1pzoie׎y@aK<*Mhyڙ"c޵w%:`>YK ȧI䑌9i.'̈́Xy~ŤqЖ|(gM}HbFzW.<KkF. ~ѦƅH4F6u#]zosI!\DMq?{bxhz$#>W?c*9L٥txf{)y`3Sqk2{YBpExyXpBu"Ymp:ʛ2"vg?J:I(8M3[i9|ݞO+ 4# mAȟL͞'I`TF@|=qRSpֺb ++\[< / N?yy娯ɪڷ>!Wۑ=lBwnX1[C<-[';DE/.?'a3(_Rs HYt"*+5<0zQ_%B-SFAأS,Bږ.58˚ޙ5 To4mZyBZm[ћoYIQW sHys4[6=T(IgsvdcHf.nm烓Ɔ"?e8^t=Yvm:!|To3:lnJ5 ێ;2X}ڱ_Rl%̺JճoP~aY7 kN \0=;$6w_4'4m* _;:Sw [3nUIx$_Sq̱:Lf{ȥT$\~:!n)o@w)ґ "|w_}cnf??wl=r-s2E}M'P!Zz߸@ڡTj틙O_!2'~e8p(]"["z^p4܄ 隣]wksֹtZ䯷hCGatObY[[ Ss ،Z y2*ӂbk>[\x| 2-E)σ=Gg)j>\z![ Ps65٪%׾<Kf.4鸋'GGE]7zӎ- #j90vlOW"UrKKuR>`sS3?p:liNz ]u nO\ l"7+ԗg{K <[À۾.j+} [@ (>EX]؁1O ./C|!t (wc6cd M9]- mN1 e&!%?> "׃^O4bk{ ~E%<&Aze:Gql K tfi*:]gy~F+0~q^TXo77H=<'xo՘<[g_x[ WI H+>=z^ ݇r9 .O{qu_0 R#m`xX׶3&ass0 US%gHlfl`x2oEL97-=4+쑠}Ι{*KYhSN{Ǜ硖.<ޤp307A} |>g6k' lG?^9΋!ƞ_~UzO.oG `ܳeT+3|h:CE.?zA]0_OA[6%DNL"K۞ ]qY~;v;OX.|΅pS3 0YO%` )%`v.0K?a>] Fu+/ny `w눜Σ\u6_,(E&$m`qSط[5g}F5jG3h<6QW'p&ʆQ~C=]!SY I2e8 e$RlM`35-&%I5S4ӠRlٮS׽\:mj\rШ6:oVu:>9]S@>7֜lNj>k N*&{ZV O{dt+~ mlO^\Y>?Vo:;Nں%W .-鄩cO/~/X2|u#O>r!4;E[7eaԸq٤5QyWֿyk>{1R_޸o׳IޑZﶷ]3}svj,{Cvi_w/|5O8w{,%63ݲj][&u!:\Çx=[TmϷpu].;βÀfG?7u|uGqo8 שi<2[_kUT~՗[}/?l[vn|Wѹ/`^M/Fv]Z?o{RwM):Y2 ۞:Y2GN,_#U|J,X~::Uy穟ɽ>o w9lhJS|rUSs_qf95]v[W{S*i|[O{w)u_?0U5OUk?9N?_5oſ3﹟S߿;95ă;\n6k5 [E=R_l>Ɯ To_ITb'mY;eW|O/N `dK~?lc=MUwӢ.8ٝ|ܟc~ cܟjuW-i63< ōda*֯4rstrʝG.K{?rV{{Nڿ廤=,:9U_9G;4ٺǿY?MU/{cLj'NIǖ#U᛹*^oʼ>~Z:]'=$sS\'9_&5a;f򃷝<}b?VsF7M/w8o蔎I=}o_ߨZ9_|.U9{#V|*;̃);)l 6u=i<󮚋R^}Sqn5?yQ:~ס~OGV}5gJ R;~ؠRy;Ku:7cS]'/E8OqmI%:ySێnbmz])Zxѐ^Io?:\ˆcrwyƒ%f6s_h^Rb^__0Ԧ! Yr+=0Ͽznj{ ޙzKOuZ&ۻE~g[yבY6Vz{?s?3.?': M h~M9*AR4߻m*y3{޿eGuKq[N=FɧS9,'wηT[RJA5ꞻN싧?啩ӲW58v{s~❩K~DjNu6ݑ?~lEÑ7Os9Űu#?j4œSv=洟ӱϯdroZwaz^ں*5Xdc >5(xpވZ945}{r}Z.Y]:a~/HM>-|s#U#uZ=wtaO\8;u-s:-zi/:=g)3HM>so.}΋kzmԸo̎W+|uOKx^ iߕ 7ޤCo5xț;{d7xz?f˵o%u^rڞ]7/gl6rQsSolӵmvP: W蓭zsv^궥aɎ[jpp3&l=m۩Ǻ:JuC˪ESrGmqPk Ua[A:oSf#;OݝoW %Ӊ(56?;fiˮW+ʾvYTphvT!Rʿ=z3Z׶o|t;຅ELz\l=aGnTnE8?s?;{lv^V{cqr̮hկOz9ꧤJq ֶ+q3X=տ>;8[jPPɰ'[Sh7T4gN8y޷N5]OÊ>7E:N*W|q׎]9[ϩy'g1u+;_Rq慓Jf%b)S-,[?=?iϙ՞-|INYMCv|x:,Ϳ|̓N6U+9qTG[%|h}m8fW+/(}ZAt|=udϧ7xlzs?~¸}ZM/|]9w3w'_?&ʖ8oqٺeIKQ}Ҹ+Esޞ_ju+֦ͮ6?O&_bc6CVoq=9IM?Z_.Ȯkh|8=Zݠ7-MGѯY:pڌcY=xg2#)S'_IZ|;w[6|u4d')dc=&/J_&kVz}aJg=yqV/<.k>%nj@v>i \25~?jwۨ!] ~'J n|3aS%5;zn-}F*Tgy0G_7NxpRhp/ZMhra;Sh=,.Ð95n/I-[7aW|n ,NOfHe~QrRlӻ,TxN-#T-L~wcw/K^swW [ai*uo﹒CRTtޘu fWO{ui X{թΕ;'Y0;X?ӡuvU[/}.SOcϯǣ7lWm:;nm6!-Sœw(q_#o:ſ]ܫkr\uٴ'ޒ{9AGp9W˕9ʶ[*8E<6j4vmSV>lTvINYTu={_jE[&m껿TR[.Vk*oNTۢcK/T&A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >A}x >J+wT렼C??I47+ݝ{+?f}lfzyy%Ί:a䈉Ǝ5n16zo*}C/z~Q?%Pshazam/data/U5N.rda0000644000176200001440000014113413670240241013550 0ustar liggesusersBZh91AY&SYVdT~@> aVc"A@ @ A  ` `Al`   60@  AAA  @A@   AA A   @ ( RQ4bF ԧ1#LL 04i A'~T*Mh  "7ꪧ~URL#O*OUS~P jlUT"Y|v3mۏ˻ZַA;"(:P~yG?{,,`` XX|,,`UUT` XX,,`~I$]I- ?{{P"0t>*Q%vWJEso~u~8pkZֵ{πI$րI$@$I$Iss*ZU $>o{Ujo|UkZ{UVo|UkZ{UVo|UkZ{UVo|UkZ{UVo|ʪ$I`I$skUUwv kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾a$II$UP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{I$@$IkUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ I$I$@x*ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~I$I$ ֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾a$II$UP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{I$@$IkUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ I$I$@x*ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~ZU7o{ڪUP~I$I$ ֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾jֵU@ kUT߾{_{I$?wwwwvI$s9I$>I$I$I$I$g{U33rvE\ݮQn(k[-rvC32}߇$&vE\ݮQn(k[-rvE\>n;\ݮQn(k[-rvE\ݮP̟}zI7wvIQn(k[-rvE\ݮQn(ffO$$(k[-rvE\ݮQn(k33'}{ޒMݒgk[-rvE\ݮQn(k[=I&3-rvE\ݮQn(k[- ~wwdrvE\ݮQn(k[-rfd{ILrvE\ݮQn(k[-rvC32}߇$&vE\ݮQn(k[-rvE\3$I&I$@ Pg˻I$I$ 2^ff}{ޒO߿~N(k[-rvE\ݮQn(T+33}ҧwUEjQZTV+UEjffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffO$~$Qn(k[-rvE\ݮQn(ffL̒I$I$@g˻ $H333>=I&3-rvE\ݮQn(k[- ~wwdrvE\ݮQn(k[-rfd{ILrvE\ݮQn(k[-rvC32}߇$&vE\ݮQn(k[-rvE\>n;\ݮQn(k[-rvE\ݮQ^d~$$(k[-rvE\ݮQn(k33'~$&vE\ݮQn(k[-rvE\>߽I&3-rvE\ݮQn(k[- ߮zI7wvIQn(k[-rvE\ݮQn(ffOw{ILrvE\ݮQn(k[-rvC32I?~߿MI$ϗwwwvI$3;k333-rvE\ݮQn(k[- ߮zI7wvIQn(k[-rvE\ݮQn(ffOw{ILrvE\ݮQn(k[-rvC32}{ޒMݒgk[-rvE\ݮQn(k[]n;\ݮQn(k[-rvE\ݮP̟}wwdrvE\ݮQn(k[-rfd~$$(k[-rvE\ݮQn(k33'~$&vE\ݮQn(k[-rvE\>߽I&3-rvE\ݮQn(k[- ߮zI7wvIQn(k[-rvE\ݮQn(ffI'߿~L$I 3݀I$ jwwdrvE\ݮQn(k[-rfd~$$(k[-rvE\ݮQn(k33'~$&vE\ݮQn(k[-rvE\>߽I&3-rvE\ݮQn(k[- ߮zI7wvIQn(k[-rvE\ݮQn(ffOw{ILrvE\ݮQn(k[-rvC32}{ޒMݒgk[-rvE\ݮQn(k[]n;\ݮQn(k[-rvE\ݮP̟}wwdrvE\ݮQn(k[-rfd~$$(k[-rvE\ݮQn(k{$II$|I$nəwwdrvE\ݮQn(k[-rfd~$$(k[-rvE\ݮQn(k33'~$&vE\ݮQn(k[-rvE\>߽I&3-rvE\ݮQn(k[- ߮zI7wvIQn(k[-rvE\ݮQn(ffOw{ILrvE\ݮQn(k[-rvC32}{ޒMݒgk[-`3'~$&y]n߮zI7wvI^fd~߿~I$@g˻ $H{fs`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̒O߿~$I$@g˻ $H{wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL{$II$|I$&ffgK{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̒O߿~wwwww@I$@g˻ $H{fsL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&ᅬ߿OI$Lwwwwv$Is9$I> $HI$333$I&I$ϗwwwv7wwvLϾ~$$?~߽o0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̒O߿~wwwww@I$@g˻3;k33߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI_ᙒI$I0I$@I$A$I3݀ $HI$@g{>]n߮zI7wvI_3'~邏߿32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI32I$`$I.wwwwvLϾ~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`$~ߦ$I 3݀{fs&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n?߿~$I$$I.߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwd32I$`$I.'{w{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&yI߿~nI$wwww` sڬֹs31$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`$I0I$wwww` swwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL{fI$L$I e ={K{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0=$߿~7wwt2I$9s|I$@XI$߿~I$@ $Hwwww` sڬn߮zI7wvI^fd~$$/32}{ޒMݒ`>߽I&0 ̟}wwdfOw{IL3'~$&y]n߮zI7wvIks3$I&I$2{~$$/32}{ޒMݒ`>߽I&0ݜ[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9ݒI$ $Hwwwwv.wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jkU~w}fdI$$I $I.I$I$$I$~sd?UUUU33ﻻww{wwݻUUUUUUwfw|UUUUUU7ٝ|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfww}}}ۻI$wwww` sڬֹs30nUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{I$I$@˻g9swwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻j{fI$L$I eI$@ۮ`G񙙙wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUywwwwtI$ fs>fs֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33$I$]݀39sjnwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUU]f32I$`$I.H0VI#UUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪kZֵkZ$I0eI$s$I $I ??>߲I$$IsU9jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3I$ $Hwwwwv-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{ֵkZֳ]{$I0I$wwww` $I$E?n Iff*fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33I$2sU9gwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZwg$I0$II$@˻I$$I!˻-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jٙUUUUUUUwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUukZks3$I&I$2I$H0VI#UUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfww}}}ۻ$I e395snUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{I&I$2s廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUukY̒I$I$@˻I$(uUH333331UUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{}}nI$]݀39sjZ9߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪_ewI$L˿˻I$9s<I$@` $Ww߿dI'I$$@ۮ`G񙙙wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUU]kZֵkZֵ{$II$ fs>fsϼҪfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;I$]݀39sjZwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ932I$`$I.H0VI#UW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪kZֵk5I$ $HwwwwvI$I$E?n If*fwwn~UUUUUU^s3;nwwZ9|w}ۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU2I$$I.I$U_~fsۻ߭UUUUUUW[֪fg{-wvUUUUUUU33ﻻww{wwݻUUUUUU?ϗww}򪪪ﻻww{wwݻUUUUUUy{廻jfwwn~UUUUUU^s3;3$I&I$2ww[֪fg{-wvUUUUUUU33ﻻww{;33ﻻww{|w}ۻ߀{廻ffg{-wv;33;nwwٙwwݻ[vffww}}$I$@˻g9sW-wv;33;nwwٙwwݻ[vffwwn~33ﻻww{|w}ۻ߀{廻ffg{-wv;33;nww{fI$L$I e-wv;33;nwwٙwwݻ[vffwwn~33ﻻww{|w}ۻ߀{廻ffg{-wv;33;nwwٙ}}n$I e395s}33ﻻww{|w}ۻ߀{廻ffg{-wv;33;nwwٙwwݻ[vffwwn~33ﻻww{www?I' $H9s<I$}߿~$OI$ۻunwwٙwwݻ[vffwwn~33ﻻww{|w}ۻ߀{廻ffg{-wv;33;nwwٙwwݻϟ>|ϙ̒I$I$@˻g9sVk\9 ϟ>|w}ۻ߀{廻ffg{-wv;33;nwwٙwwݻ[vffwwn~33ﻻww{|w}ۻ߀tI$ fs>fsۻ߀{廻ffg{-wv;33;nwwٙwwݻ3/ﻻwww}wwݻ߀뻾{廻uwwn:nw{wwI$`$I.9[]{-wv~ﻻwww}wwݻ߀뻾{廻uwwn:nw{ww|w}ۻ[]{-wv~32I$`$I.ww廻uwwn:nw{ww|w}ۻ[]{-wv~ﻻwww}wwݻ߀뻾{廻uwwn9I$ $Hwwwwv7ww{wwݻ߀뻾{廻uwwn:nw{ww|w}ۻ[]{-wv~ﻻwww}wwݻ߀뻾{廻>|{fI$L'{.I$@-wv~ﻻwww}wwݻ߀뻾{廻uwwn:nw{{33}wwﻻwww}wwݻ߀뻾{廻ϟ>|ϟ3332I$`$I.9ϵYsf`gߟ>|ww|w}ۻ[]{-wv~ﻻwww}wwݻ߀뻾{廻uwwn:nw{ww|w}ۻwwwww@I$@˻g9sVk\9[]{-wv~ﻻwww}wwݻ߀뻾{廻uwwn:0VI?Uw{$]*'@^{$I.9ϵYpY$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=W]_{wwww`s8I$@X߿dI'I$39uUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt {Lw{ހ.9ϵYUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdu+{ޠ{e395szw{$]*'@w IP`zw{$]*'@w IP`zw{$U{z${e395s̒OUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt {Y$Iw{ހ.9ϵYsf`]](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](09=3332I$`{wwwwvI$I$@ۮ`zw{$]*'@w IP`zw{$]*'@w IP`zw{9I${˻$I$(uUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdə2I$`{wwwwvI$@I$~sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=W]_ݙ 쫯߀?~`zw{$9s33$I&;@wwww`E?n IP`zw{$]*'@w IP`zw{$]*'@w IP`z33$I&;@wwww` @ۮ`zw{$]*'@w IP`zw{$]*'@w IP`zw{$YI$]݀9s<I$`߿~I$@{ $~sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$]($I{e@ۮ`zw{$]*'@w IP`zw{$]*'@w IP`zw{$U$I{e3~sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](I0z fs>9 IP`zw{$]*'@w IP`zw{$]*'@w IP`zU{;@wwww` sڬֹUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt {Y${2sU9fc$]*'@w IP`zw{$]*'@w IP`zw{$]*'@^{޲I${wwwwv9}k33t Y$](0VI=WJUOUҀ.sdt Y$](0VI=WJUOUҀ.sdt Üf`_s$I0z I$I$(uUOUҀ<ٛ'/}w3wz$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}z>VI=^I$Lw{ހ.$IIUdt9fIP3wUY$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ'yn{ʺ9fzUdI{e399'@sUdt9fIP3wUY$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt ~{I {g˻ $H?u߿~$I>;@wwwwv9oyn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}z>VI=WJfn$yϵ=3332I$`{wwwwv9oyUVff(}z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}{|9̒I${]݀$IUdt9fIP3wUY$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIPWI${]݀39s㙻z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}z>VI=WJ{I{2sZ$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}_s$I0z $I$sfIP3wUY$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$]($I0z f㙻z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}z>VI=WJ{{˻g9s{jOUҀ<ٛ'@sUdt9fIP3wUY$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ~{g9ffdI${2I$I$sfIP3wUY$](}z>VI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$]9}I$^$I 2I$@qVI=WJfn$yϳ7uUOUҀ<ٛ'@sUdt9fIP3wUY$](}z>VI=WB` {*?ο}]])/ P^;W7ўOcJK֤rP[r" Woo313%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TT P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T ST P%@T P%@T QYP%@U$J*J*,J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J)UT7T<?f&J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*JҠJ*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*J*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J**J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*%@TYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T ST P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T ST P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%OP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@?@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TT P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T ST P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%OP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@?@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@T P%@TVT P%@T P%@T QYP%@T P%@T P%Ee@T P%@T P%@T P%@ LLN2P]]\ɘJ*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*JҠJ*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*J*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J**J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*J*J*J*Jʁ*J*J*+*J*J*J*Jʁ*J*J*+*J*J*J*J*OӞ5J" VʨȂUJ U1AY&SY[)H@@⨃F?0@@ PPPp ȒII"I0@@$$$DDHII"I$$$$$$DIE$I@ $EI$I$$=ࠐPP @@JH  PH (!T A@JP   (VQ=STMMFC!hg4zbJzTHMѠh3HR~%Ojds`!&UM6hDU; UT>GRS99:(TEP_]~N9}'ﻑoi *h~z^VffI$?'?'}[z=G]E?Sr D"]x:u {I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$̙I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I3333333333&fd̒I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$ffLǔL̒I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$fq;I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I&fffffffffz33$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$fdyN$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$32fffdI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$'I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$IǔL$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$33&fffdI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$Lə2I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$III$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$Lɞ8I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$33333&fd̒I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I33&I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I'3?o߻~0~ꪯ_^z=z=6mmʈ"(?X@{ߑ>xz^Wxy`8xxyx)$ ( 떁@QD P@z0O[mUU@_`3>0k 0k W̪w;9^Uyʯ*eV t5`a 0ky瞀~}}|eWA] t5`A] t5^Uy0k 0k̪UyUs;ߝzyʯ*eWA] t5`A] t5מ|{C 0k 0kUW~ws*9^Uyʯ(A] t5j3UW̪n gϰ*mqBP% BP% Bm;m(J(Jnvی% BP% BP% J(J(Jmn0% BP% BP&sa(J(J(MmP% BP% BPmq(J(J6۝ BP% BP% Bm;m(J(Jۯnݻwww`UP~g09_Y(J(Ml&m(J(J6m(J(JmJ(J(Jma(J(J(Mm(J(J6m(J(JmJ(J(Jma(J(s9_Ygs7~w} 6mmP% BP% BPmq(J(J6۝ BP% BP% Bm;m(J(Jnvی% BP% BP% J(J(Jmn0% BP% BP&sa(J(J(MmP% BP% BPmq(J(J;v@*ys>(J(J6۝ BP% BP% Bm;m(J(Jnvی% BP% BP% J(J(Jmn0% BP% BP&sa(J(J(MmP% BP% BPmq(J(J6۝ BP% BP% Bv׷nݻml6mmq% BP% BP% J(J(Jmn0% BP% BP&sa(J(J(MmP% BP% BPmq(J(J6۝ BP% BP% Bm;m(J(Jnvی% BP% BP% J(J(J9{UT`J(J(Jmn0% BP% BP&sa(J(J(MmP% BP% BPmq(J(J6۝ BP% BP% Bm8xgs>\xg2ʯ3UW~wUT 0k 0k9^Uy{ހ g2ʯ3UW`A] t5`A] uO<UUUyF 0k 0kW~w{ 0k 0kʯ*eW^g3ʯʀ~UUk½w 0k *eW^g3}wwtUUs*`A] t5`A] t5UW~w@0k 0k UyUs*9eW^g2k 0k >wUUs( 0k 0kUW~wA] t5`A] t5`A]}C}}z*Pk 0k W^g2ʯ3{ UyUs*9 t5`A] t5`^y<UUs* 0k 0k}}UU@ 0k}ϟ7 0k*eW^g3UW̪UyUt5`A] t5`A]y<9^Q 0k 0kyUs;ߝzP0k 0k*9^Uy{ހUU^g2ʯ3UWA] t5`A] t5מ}<΀UU^g2k 0k *g{@( 0k 0keW^g2ʯ3`A] t5`A] tUyUs;ߝzA] t5`A] t5`A]y<}UUyʯ*A] t5`A] t5ay{ހUU5`A] t5`A] tUyʯ*gP}k aᮃ 0kyUW̪0k 0k W~wUF 0k 0kW̪w;}}}k 0k 0kyタ9^Uyt5`A] t5`A]Us;ߝzUC] t5`A] t5`EyUs*9џ?>sz=UY}}UA 0k 0k^Uyʯ*g{@*3UW̪5`A] t5`A]y<UW̪ 0k 0kʯ3{ k 0k ʯ3UW~wUUs*9^Uy t5`A] t5`_}|΀yʯ(A] t5aƺ 0kyUs;ߝzP0k y{{UUUUUUUUTW{~Wuus~_z{yUUU@UUUUUW{wwwwwwwwwwwwwwww*wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww????? wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*wwwwwwwwwwwwwwwwwwwwwwwwwwwUVs*wz@@}@ t]Vs*YUS ?@@>|U_UU.@ t].{9t].@ Ug3www^T>@ t].̪}t].@ Ҫ9UVs7wwuUU{ʪ9UVs*t]9UVs*n@9UVs*.@ t]|ϟ=UW̪t].@ ЪU@9t].@ g3www^UOt].@ UUg3www^@>@ t].Ug2fUTUTg2@ t].,n@.@ t]UUY׾].@ tVs*n@}}.@ tg2eUU{U^2eUU.@ UUʪ9UVs7wwuUU{ʪ9t].@ ϟ>|}UP].@ t*eUU@3g0`9s nnUT=`9s 3g3@̠ n3g0`9s | ~g0`9s 3g3www}0`9s 3g0f=`9s 3g0`@{9s 3g0`9タ9s 3g0`9s7www}s 3g0`9s n3g0`9s }3g0`9s 3sg0`9s 3g3*sg0`9s 3Ѷ ?h *(B(@T!ps99}q3332I$I$I$I$I$I$I$I$I$I$I$I$I$I$32I$ffffffffg32fI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$Lə33333333333$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I&d̙$I33$I$I$I$I$I$I$I$I$I$I$I$I$I$I3332c̙I$ffI$I$I&fdI$I$ffI$I$I&fdI$I$GI$̄{I!$3 33&fffffffffffd$BI$$BI$33&fffffffffffd$BI$$BI$33&fffffffffffd$BI$$BI$33&fffffffffffd$BI$$BI$33&fffffffffffdI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$HI$HI$HI$I$HI$HI$I$HI$HI$I$HI$HI$I$HI$HffI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I $I $II $I $II $I $II $I $II $I $̙$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$33333333333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!3$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$fffffffffffBI$$BI$$BI&ffffffffffffBI$$BI$$BI&ffffffffffffBI$$BI$$BI&ffffffffffffBI$$BI$$BI&ffffffffffffBI$$BI$$Cs^zGrI$I$I$ffI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$ffI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$ffI$I$I$32fffffffdI$I$I$I$I$I$I$I$I$̒I$I$I$I$I$I$ə33333$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I&ffffffd əI$I$I$̒I$I$I$I$I$I$I$I$I$I$I$I$3333333333=DHI$HI$L̄HI$HI$L̄HI$HI$L̄HI$HI$L̄HI$HI$IfdI$I$ffI$I$I&fdI$I$ffI$I$I&fdI$II&fffffffffffBI$$BI$$Cuӎ833333332fC33 330 q33$I$333!$I!$I!$:뮳333333332I!$I!$I̒I$I$BI$$BI$$BI&fffffffI$I33$I$I$I$I$I$I$I$I$I$I$I$I$I$L̄HI$HI$L̄HI$HI$L̄HI$HI$L̄HI$HI$L̄HI$HI$L$I33$I$I$I$I$I$I$I$I$I$I$I$I$I$I $I $I $ $I $I $ $I $I $ $I $I $ $I $I $$I33$I$I$I$I$I$I$I$I$I$I$I$I$I$I3332I!$I!$I3333333333332I!$I!$I3333333333332I!$I!$I3333333333332I!$I!$I3333333333332I!$I!$uӻk;]I$I$I$I$I$I$I&fdI$I$I$I$I$I$I$I$I$I33333&fd$I$L$I$I$I$I$I$I$I$I$I$I$I$I$I$I$L$I$I$I$I$I$I$I$I$I$I$I$I$I$I$L$fffffffffd̒I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$333333333332yY333333333$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I&ffgI $I $II $I $II $I $II $I $II $I $II$I$I$I$I&fdI$I$I$I$I$I$I$I$I$I$33!$I!$I!$333333333333!$I!$I!$I$I$I$BI$$BI$$BI&ffffffffffffBI$$BI$$BI&ffffffffffffBI$$BI$$CO)3333333$I$ffI$I$I&fdI$I$ffI$I$I&fdI'xII$L$I32I!$I!$I3333333333!$I!$I!q$u $fffadI$32I$I$$BI$$BI$$fffg]uffffffBI$$BI$$BI&ffdI$I$I$I$I$I$32I$I$I$I$I$I$I$I$I$̄HI$HI$L̄HI$HI$L̄HI$HI$L̄HI$HI$L̄HI$HI$L̒I$I$I$I$I$I$32I$I$I$I$I$I$I$I$I$ $I $I $ $I $I $ $I $I $ $I $I $ $I $Iۛ|^g=ٙ$I$I$I$I$I$I$I$I$I$I$I$I$I$I$̒I33333333332ffL̒I$I$I$I$I$I$I$I$̒I$I$I$I$I$I$I$32ffffffffdI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$q!&gI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$$BI$$BI$$ffffffffffffd$BI$$BI$$ffffffffffffd$BI$$BI$$ffffffffffffd$BI$$BI$$ffffffffffffd$BI$$BI$$ffffI$I$I$I$I$I$I$I$I$I$ffI$I$I$I$I$LI$HI$HI$I$HI$HI$I$HI$HI$I$HI$HI$I$HI$HI$$I$I$I$I$I$I$I$I$I$ffI$I$I$I$I$II $I $I$I$I$I!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!I3333$I$I$I$I$I$I$I$I$I$I$I$2I$I3:뮳332I!$I!$I3$I$I$I!$I!$I3333333333!$I!$I!I뮺3332dHI$$ 2BII3333:뮳332I!$I!$I33333333332I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$fd$BI$$BI$$ffffffffffffd$BI$$BI$$ffffffffffffd$BI$$BI$$ffffffffffffd$BI$$BI$$ffffffffffffd$BI$$BI$>889<ߞW=I$I$I$I$I$I$I$I$I$I$I$I$I$ffI$I$333332ffL̒I$I$I$I$I$I$I$I$I$I$̒I$I$I$I$I$33&dI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$̙33333333332I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I $I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$̒I$I$3333I$HI$HI$I$HI$HI$I$HI$HI$I$HI$HI$I$HI$HI$$I$I$I$I$I$I$I$I$I$I$I$I$32I$I$II $I $II $I $II $I $II $I $II $I $I$I$I$I$I$I$I$I$I$I$I$I$I$32I$I$I333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!$333333333333!$I!$I!$333333333$I$I$I$I$I$I$I$I$I$I$I$I$32I$I$I&ffBI$$BI$$BI&ffffffffffffBI$$BI$$BI$I$I$I$HI$HI$L̄HI$HI$L̄HI$HI$q$33$I$I$32I$I$I33$I$I$32I$I$I!$I$I$IY $I $I $]uI $I $II$I$$I $I $]uI $I $$뮺̄pI$$ 330[8s"E ""( ~^p w_?_G?ww{(}  |~=TP<:t^^ײ<"8}W_P;Nowo'|"P۸@@~nK>cPT<(=Q !0 !( J H0 ?"x"EPw$S  shazam/data/CHARGE_MUTATIONS.rda0000644000176200001440000000170113670240241015530 0ustar liggesusersBZh91AY&SYo "Xp@/ߐ@>ٽUU^JFA ɠ4?zT5OH~Ѡ"#ߩUF@%P@2DDi2d膀44Q"ǔP@vT;P0!؝yBC!a`Xt$Y"X$e2dȖdƐ,!)wn* rx)<}׾m6z Acxs6.kq]uwpl !DA@% `H̐-$JV[)mJI ZBhI`$HH(\AEr(RAА(-$ -p̤&V,D I (UE*`nH@ @$$FBI@X[m!1! YVk]3'/o , , , n 7+emִ95(J@D D%͢! !@A"}E"dDSz쌒&߸/OWWVZ뮺.6%ݤI$pgm{r'9sD9$V$8 3I$54$JĒI+3I|6`MT4+DDDmbI$$V+I$8 3e)󡷛mv$JpgĒI&}\kqeZ[S%zebI$"I$g dߔ IZxؤOOk2OPU^y珤 7P5:ߤnLv0* d?2WA&oׂ.#AsL=]@sd! /7ϵ=gſESp H  Apshazam/data/IMGT_V.rda0000644000176200001440000000102313762520614014165 0ustar liggesusersBZh91AY&SY:&;v@ @ZU@6T UO2j O1OSzQ馣! M1&FziSMh4d5S41d2Ad6544@R BAD(Zvc 5?}DeH*YDӲ6,݈#TW /4)KԢkAȗ4Wx!1%pi#SZݺB5}r }WvݚȱYYo跫_ m]T5@2h$X8bV'{10D{**<>~+Հ|AbiEVzw_;RѬ M\;$Φ:~>i=KsI*!PNrQVqUMrhu *2XGѺ搦M2u ˍ0#Ƃ،D&<00,D-.X{ITDa T%(`gBZAat+,əTa\@&Wr-yb'"(HDshazam/data/HKL_S5F.rda0000644000176200001440000043340513670240241014241 0ustar liggesusersTU7|Bl D n%  ;PPEΏyXw(>!&)E]GK=15%% RORKMLRٿzWĨHظq}RzQ>(f@jLRA}pORvJNLV?>&?R3:2_ǟD5#nC>^]LV]$uvccQe8Hݵ:O.{{tcII[/>6)&25!EݱO\Brb~TwMOR?uu͊?XKWV6'QIN{; 3Z.+sn_ψSEudظ)α>h)2.F%9uݯΉI ɱz뛙hiPedlPW=.%yϞc{7300㏍OOT]y3oł)(ڨWSH׫jSЀ Cb)?KE>5Ҝ7;2 l׿M.c`xE$}N{Ev¢M&lGY|o9j❌$Ǚo?FMm3ML<춹7/?}hYz;Qe[s7Q:Qz=;kt)5k8|}Mpu3܋+yOs:CKFnܬE_.+ \yM][bª}H1U:+S0QӀq 5PhBfPD-ѤQ}m]1^0?'ɿ{"(l%evRPtkRus_ZPāP2 XKr~/ndoNV{k>ZFO>Xp<קAeMW [37ų9IcL|'lL.GV>/ȼm22W_&^{^f 3!Q[2tX3Le-Bg6mҙحcg2(JvۭJ6œֆ*&O3Bui>K6+Ku!.Ns9E1Ͳդ|Wɹ.}VqC!kDK?R_8f._Y7cyMҥ}&vnAvKwtH!ԚF`m_kU.ާ:-{OFy~!͖5w'pE>t/XS5T4앓U·F<~-׿E/-GK}sr(ѯ}]*n,Epovї]!(zZ۩oQtR>ŵc$-5vT<_K!OFUAIiO4&:=Vx!DmN$uއS?e9 j.7vN^ ӷۂԬ>ۀ4fo͙PiYV%I^ RbjLN>֧-EէYp(':.T4_dq$.Wӳu[y>Uc+м1hN6ZSBhP/ee-䰷SUrt~|b ;mжS)2#g'dӰΩd7˯Qؕk #iݣyY*}|-yo+d-n+Iql/VdjXm"75]Nf8OQ950ڎ;|N$ޱ#SaE$s]sΓ2wFUy2O+kkxHßU.өjgr^Pt'rCr0Y6r.-ڊoSCM> HQN F̤`MmN?_$i:,/+*5;|Cj\\4;b<zjj]WKg=dhZqF[d΅?ml2sړw%qMƓ#f' >rkp}Л+OgOVo^PUsE}?cGHI`6$ݝdtnh]6죶8JSLU6:mO x7ىBշWXR1ok@"1-un~_aWKa`G ormwjĥ Ps=V#/ 0lSjқ"dP C/`}n~? wj_T `ݴdx**8ra8[9`$_xzE~FO&vaټ:yk?ߑi?'xg~ySuxAsW?y!yk|.-K+"V^~DiW"Ǧgl$9;x6tץr9ݏ,kLuEƣ8cUOHhj:CI7:ydtD`5r}'^d%})93QakGWsg^F͙C_YNO=ׇbbOϓ](RhԊ7?euzE^7/rN^kN=|LN?3̬)˗zRU/6&:̹zvhft^ӕ6 ܽMkS)E!yYRܣ-\<"<{x#׮9YJ˔*GI]EFR^~"nzۺ?r>Tza&~{t(|&g:m1n C܇e޽FcFaAN^V;`U "(YE>{xE;ryx5Ȩd}dm@]nhL: 3E"ݣ3 ; L|WDFt\~˫#G?Q q[~ڷ7[O&/kI`=/:Ǹ4X#^Nݡ#8JӢddn1 .?=>muX$Љҷ"+wSRә}$%Ͻ[@-rtGξ̃d?qYuyCdk'ND 7 r$9l2vlTF6[?\Ř/eNoLsq--l}tri>b$9;]ENzyg|w:م:Yբ0+zYd]-vzCf<˒d5=M[jPLy:#|]{ 'O 3mK~!kO{LGf_Ov_m'Y"C.j#/%;gWe+fWˋfCq,2 Yd:LhB,\]`#Wl ryA!kzϵ_7 q ԩX>>#}PhUqJwGB J\6дF/ ^zYs=2멝hYd um1ٽu3dumLSs܋d:*إ?*y[vSťCj-)]㖓Ѳ.mOU)ލuf%J/ %G"W_I6%8}~B]k׍AN+W$" YOpCYt.Y?pk􍬎ti~ި\eD6řxM At#霿.8>aM'*(lx'[}s{>doՄDBmy2z>]>^jYej{ܦ5Z,`y4V.&ӄMY2.u_\2ƇWoNz1jWCNj=F~_yi8{29Wtr_b\vuP$mVC$D ፚ\'a'ߒgRVNEVm/'?_&';ֵrjHydød|jӐY4o`Hc~SMozdkt=%SmrZʚi~z-G]p19mt5nֽ)2ք [6lb}NC)3!)0VEt8;͊Z}&(]误G\DnkԿ;Ķo>FkV_+dqѦլ/Ȥ{ZosZT#cN$ief&˳ʢZd!o~z2YnKydqqkg%.zQƦ)U]]ȑ-wv'9u|sD #U˹9;?amd?b։ˌ~,-?k0s&O8-cjp _4a](ܫVUUw4F!z#yX5(CSB3{Q^?RLa#ˮOZS G }M&ю"NyM[vλ\[Y7g+3}@GώKuVT;2͌LYKzZ{E,ڪ69@yڔ$2??<;zVm~i4r}fdxFϊǻyu}roS[V3ϡ _#Ú:[Kv-֢Z%O;zc!tmeی ~|"C$ cnaɢv yYOJWv2lO8or8j֢~V/ 8eWEs |Sta01eDl;\s2>sf2!$һ`cdhKa;W}~%W\L_B6Ӈדu( Hcji7y&}Sj^Nk&;X[f<"k~oۧcN- ?w< ,,GgTv82ҢȞmڞg-`Y)`¡[=_[WԈi?ҽ.T2;9Okw٬5Q4<7xpt6/+oA;8B- vYEwfwBq^Gu >9%*E$?6}JIS&O 31A;J jtxNԴg+a?}ӏtGu[dB|rZ=5b~?vPMHKQ^+Z\fv٤Eèi@[zj8s窐`3E|."7YI]f4XqZsR8}9SK)[.YLF'?$tv7'dhE>d?,A3@3S2G7$=PWEG5gz?EOE^Juw򐝮Ķ9|My:㖿lt:Yqyu6Scm:82~^SPsy"ҋ|wgo>OPP/4<,C09uT!٦-XҎp4>lz>y~9Iv'WxI ұW w#ww#==FezYSp\KKvSIf޲ceFNI]]#'IoK޸OΘzA]U8Dݙɠ _O]L #9nhK/?2zaϞ7¦M7óݮw{zּ@HJu$OrcjuAgրo2wV*'klk{z]մń+/к݃$u1;ﺽehp_6dbxCrcw˫F:ȇ;Α(i킾/d_Pz]ݸ5ulAӓɯAX ?fQ1m<}9&a8ٺ3NOUNmJ y.Y*kqx?Q?u] m2^Xumv R2 ƦeVftvdFR|Mo|=Ot"ڟONqquk!%Ī]j8e$ju\Y|鸦.9~>9\7oەer$!8"u|Iagnkjkk_omvc^uvnآJ?w4ćkSoٙTh+\zWyu)M掚 5Wc-"mMnA= =3rz4:HÛۚίb|"nOŽl:@ӒYVhJ25zg'߭Խy{g]w?d{idYcS -xNoYE^x)}o>F#b*[yF\Jv7?Z?`jg&R^ʠdzhc_<so軐!?E²^%~:ɱ;}}F)Egu'-_r73t'fبM[5dfeb~>8J^-_U$wR ~Wv8rI%/BMy8ozdTY*?bq=pR C](p-(h ydT׼qPE !;Wk 9Ly3&MXBWx * -8j l1$nj}Ǻr>Xv_)Wo)U;iS ZzǛtdJ ;cΧf8hǺq#.e1*9U*[yp=IN6"^)?Sko5!=Xk#(WRDq{?S.[skJB}NbFg?b}ՌCa>ZPBA m +kdd7?6C!jԙ3 <9;ezkkxmy >-9),x*osbuAWXy%KR&ֿW'xR=tˬSsHzcKdjj4id|,}pǙ}b܍u 3N8&͚1kAm2> aK)UaOȹ<~P1U0t^kvQ)zrf?7z 8zL;umn-a]ӊg9Ʌf|ףc'CHobGj hG-SoGNB7"/[VҐLݖXGmJj^íF3TД(:r?=>m8!;yi\©'>;ߌPz><ZN7LM$ԼC.d;-)`ҵA1A凒c'l ݹ`tNlՈ薅sMr|u_5֨{6Wʋ$ ZjM4AO&m6AIѳȻboyMѹLqPE?&F^71OLVezC8Ӱ'AL.n%]u9-0l:>dN4{ћSZB6˻NkmkK&:;΋J3:[fE8 Ll}y~r8y\f"Kj#;ƙ"N{!{Y6Ɣt\(k"ix5lzGV!ިdq,IE9/݉J~UJ!;sJu4VY:\tG=hj.(m޵2ur=I.9W𕌺;ԸZe5mߡ7tHo#'ZđyIѫӛmGgr;ww;P'V;)Izm߄ܾT~R:LͲ꽊$Y] Ú l=&d|lt2o_ޚiOCdz+U+pB7렶珫J{VߑSG@7TF|@6}JtA㝘)cU "юȶ\mE6ȶSړiOZ"ukkCٷm]9oXdsF[{vn#Go'%ٻѸȭךEϔ9btK܏]lQCr2~[Ph{r|rZ/pZv*3_+[YsYR>Pc+=ѹD K݄B mL^Sa$ؽk 6Xِ=jYvNjE>ޏ/'zlN JsB~^9 睛7hr[Z[{.WO=Fs7. X>j0.dE69>y_k73}M7щti"l֩tm~:j yʹTvL S&".;uMr2h'icd/e FlK٠_:mٳL|ԋh;3R}K۷_Qr+Q_DUs^WK6߫[ăj5 eFN׫א5N#s5U 2~SNYN5diǔ3$8 Z%e'Xih Y0 ֑4)yS5~\ 몵IOyIwUY ll\gj7mo_7Ņڗl#y4l>hx{}s jS-z8y޿&tt^[=3^[y2Y_ 0U$y3q=іƛߠb3} &<'.%e@- +# R95q쵊xZG!Obɼ_–C&?&RJIg8{lc hG")AzyE kL3O&;-zv\krh:(7z/Ǻw2U~ 㾃);sRHq) )(WxU&GRh̨o/ #hwξrVFy6}ł-sw{8VarM-(p'wu[9mb)v$n0lx ix+ŎX<ˣyg4\.m8s \%.]O-68@^ &F#^WgkVě7;͌X;z?okrbg5Zo7dR9R귶#(GAZS?)ZnͥnSt'wNCYwu4%jO[K!qs۹G 仚]B7:0NslwYnxA#]y'ԇ;uݢbL5'^ȂI{Ƥ+" ّq:Ѳ&[3{O&o%և7Nۈ)txsrv8<} h]/I w *+˙zZo#!;/%Z+??=>}tTswwGM,l sm~FAɯ&jVz!.y+bIY'2:(ok[^g'y&L Y&=~{E>I u;z' }x!lr>ńcC_CRYe 9GՔؼ>徃k8Zgeɂ=_3klbgglT; h/=Ӭw(9`ёefO&rygu5u?e!Gt'}KXM^ۃ%fWx*C=fV|Ygs' .~9Q#EO<$ojLI!hWI59X1G7Xr_jafY.uk%>Ҡw=rT}gϡ<B n0d\' r$ݰd?ZY4s [yB8WnNT A9;_.N ndD?ME&Y9/Ƶ} 2 xK{r6".| #Y4F wdL&m=)#O4Iw~_UwF>پuuѡ&dc5b}u߲6zPyޛSnMVlL齚!Н~~zS+^o;2H.nqiC $kMN粚ٗC4XCkg_dլK{f>O7.)ر]כ՚m;T>F8~99r 9e۞L.?N{j1xxk4rhWAm1ݮ'r+Hmi:r5$|Oe85Thx(ykw5Oyf6:SF(mԠa {Kck;[S!$ Z0]N>goLΙy[:E[$wy~ڜ=*4xP1 p5)tcف7QڈU?Vݘ"9}Lq} ߑƈ#mKΠ-h@F[.xy,\ y>jJH;5rduN;SDX U_ɶ~Ι2>7YG4BNnUvte3L}DK[ћDQN\XTsrr8CP#Z5{+Y5Y| 56>we+rJ^vy9;"ȡFѐzGމadVs!uϴfj6 |?=>m#m~r凩yqt52-٥a@ªZ/AVf/ [PʴE^Q@kzC(WJNQr~P?MenR5׵]D5'dqbwL|Pp]3{>̝-FCy5'oR$c/_ӇؔzOoǘ\}7?=>}zDn;)+,Pnw]iY1>ׯWpk إk6Xˁ)Cf"ΦU'NkH^gޭU4s{۴g]뷵)t'(\P\4mo}'_{q5y5+4Qe+~ʲ}vMWW]ۦ[I׸x'WD,ƘɵVM#kMg&}e[t ͮX$8+nsoO]iþnͮ&檉v%OkWU\<~F*3r 6$ڵjF@-r|}`I3K3Z )Gq׼M6j(87%b;DS(׍Q9d|˭+^MUG S`(,KtS"b7^Om:WSŔ0 ,W8tl4`хQhayT/hKQ\^PKn}ڻMSAn1+M>}H=fWF^G%uA[ԇL7Yx:מLnZlUN&E(_kd bWso#*uu77pެgV{WR_ϞThZ%;V_fTXCۂ.~?ZԲJ]ƺg>M7UԞp`fZ׽Л{7Qoѽھ{G{5wVWs> 6\WG ŵ&T'ͺxrzǮ;ЫQኺVg+ĵSJְ&uyT'AQN%vE~uOQnӄ e:Q|L%UQ簬фNըP7leFwEdKGC.V޿oVcG +r9':X\ڡx0acq:rD]g7X:[_~ o?nxdtSc{1%t꥝?T<E:⾇/gjn]WK>[~/=Ƨ(r$k}ϚtGTsE]~LRscHٸMTăW *7z:Z|,:u?oet;[q[{dPحt\7Ǵǂ >|q/?-N[ίytf/SEJO? ?Qy1:5Ɲiz0{7QVL"+?EzØ1jtxujb}z˳Fq[by1{n'v:\RQc‘t lgX(`j~?&V?ly K*Qx톧]W9ئ͏u`L Vu*Z+/:vT,Uf>+m[:WnUug}|'}OM?\{s zQie;>P>'* Z_wJlXX]ҭ][wm tɢ5)w',}h3+]Wdmx-/xI][yyouTL-e Ke~Ҕ?<$HyպqlPib)\r*u\R?/cZu&˨dm:_S̕nUp٢Rgr *zܸocG⿤g̡t-`;j]ܞS]TlW}.RBnX>T96GuoM{i*uguvN%W:۫%6\;ֵk<>5W9Ch=>Ŝu5W|Q6y;Ŭ%YiK F7ު_K&eT{t*Sf\ LG=!3=_MRwPZ;=AZ|~*uGrJm|F؅U;Uy$ַ2ǩOr1͔mV?ZPT߫Dߩs>FOoҊ}Wݟdݍ6t߼ƶ1t9ϵ>orJm=4"WyvO)RmiaH_ۍ/OW*VZ{{{Δ?淙DN}1?>\VoWwK- 1=Ck}zt6z=HЍgӉ4xQoWwC}BYkK*b_N(<*o.7LFG<8wOOٜQl/WvSQM1t>mWJTunNjOGߦ.*o1/WM] >u;,EqN}m>ʚќWhfln`1oSwﵤӿ`<}]'ߦpݖN_i&+]kнtmU=uz)+=Ь{;ʻuVt)`SM#uK.{\3kC:~߮.sÉ/R݂]3)͈EM6ߌ8mb#2}ɯ+W)'vz3VlCy߇t0oWw=rm )Ӳv ܗN]RMYJzmosv}ֱ߮.kFt]W#޼*=\~uUtFG'5_]uԇ˅QM]WjWͨ*^MMkjݙ_fSM9g^m.׽XO]/Iۗ|zor^6nلDǏEƎcx*bTk[oݛ=yOW n/UGm-PH}=xqUꮬYkl*uy~b0ц*u9^gJRi-ѕ.^LRwUeMAˢ=t%|tۗժ.϶ڛJ^B9~/[4›JOk-SRwu nKnbΤwz2*:lXa@ P{EXu2sbͧ.|Ãfy˳B&jC_ߢPVuڡMڛȩ8!KUz#0'ܧT:3֗E6oGk|=y]UrqTb^l!]2sH 'RnG+NYcnދʨ8Fst՛.U,fq)*m{\<[|| 9G:+ݒfOwT=A{G-wo=VMx`30X ^8GzpOzd. >rM6%_&Z>KbՈ/9K.IE[v:U>avTtD^xg_ ֞쯢ӭ]x?Gq%_17F="ͰTO{u\]*<n⼹=Z6+>(oޟfPaF'O3*?VZ1 .";rn8ޙUXr*:Vj,{Y)M:>w=p<^wk;|`ثk#U91|yVk2M0U'cN](+WsjWF^{&ks87nYntu[oyo=ҽ*ug=}J]Zަk>'ݨZŔ׵פ^Sz Ag}~5Z]8QͬRw^$jT<$7"ض˪uL[Oe3}T;sbVJzRv_+UrTU2xc{s*pbAB{OS.Tj=]ܺ܋ҬTL—u˛'2]U.wD5WTZΝ\HCST;H+YGeuGiSN˫/}Otylw󣟫=Mv{JW>fgfK&҃f__@Z&,oRx%vWz廳G5 R}v¦TN厫.y*m tMJPluZ1J ~{ޖr-~ָJA+n;8Q kt+ǯ^x~b1JR7v,B*Oϗ]w4]u~]W4]: 29yު]=kTz3oRŔ_W=?'>Uo㶭wx0 WJY)l(lA%:i#;e]'RSQ;^^M>լRwT݄TbƖtVcWU@?GQ)Rn?/TQ ^EET*z&U˥5o%^ M}f~PZ)g?O[t_'3xfulYˊ Z+zO;Xv͝c'gDzn>2]U'Wp1ޞPɡ~KSVS~kGrby:{BSX_VZwrS+ꂚQ1QOXVEZSN jl].>j Я9}qs4m]xj]T7mƔ@U'Ur2:ԻH%gPnZp⼏o+*oMwq=d6l>j_yn#_7[Vq@6t讎wysw5ͬ+^>)| G[:o(oMNR=4<-%5;Gq3?3A}6E]|:ݽc>\hMQZL(o]R(˕߼Ȼ)x:[7;6T\b[nݓ3{=UvfXw'v|%-W Z)޿v}߲S_{MuY>u lV96ҘOI~7,@qcݵ)3Z={nNs>wj#)pcQ#W^ uq݊~cSƘ=÷*>_ؒAwW<.Ckߤuܹi='͊v 6;nX[{WW^[f]|_yq պ vu_^޿Zg:e#t~v!uYiokс??NCYcʛSuKE[@}=AQ~(ꌾO u_qC;r]sm6khRvvZryIz]M{.(z||Nuš˔lm^xbhͮ-ܦKtK[q}7֊m]ca'e>1shq ZPoAzŇХ6̶ Sԝv.Nqy *R=wbѯ+JnM` e?IavT;fw/T>cA@)|,諭͖fM՞؟r1_uyۃ)-i97?l?67^a#ni?^_W@ΈHgLx:/Mo{CmN6Áҥdzcb辆ֶ*Rg1===g_/!a?}חOzuܬYEݻ̎ѾwUҿzʹG}:e].Ҷ=\⪾ T9%`z at퉣U_^_@Gl7ݝ@ )V;www $qt%Gkvbg|eL&&ts]?/x^{fN;nΕ}zg3JXJ/B~^Sfӌ-gڟ⻿0ut޺+|ΠFVػyͣo[dž6 ڏ,ֶ,ENk}f/bmKQ۫yAMn̈hKaNnC/uM6Ts݀ۑwY6̥owjݢUKpO'"Ci+q&{sN}=7 qˈXB?OKe]s! czd%Y:Eܴu^V|3,)f^_WL)QWWDOQ,tn4#ztʏcgy^rTiRߑF\1-*8_V%:=0%:TL՗?mu>~~%Y޾Y,ujex%GuᾁǩMmB(PLg3;뾠G'*nޓrR=o7o6K;f_Qh]f4+f. ~*t}P:8@9F冷󱓊koAHMCF:84r^&~ެtBA :R"pOu<;~3.lmE6ɀCU f^?RVw%NG2~yKԘ?G{ 5/~m[7B?S?q/et҇/PcK-QetWWNJ/?ГOg]h: xpTgr(:]G,tޯۼ2~c(Ќ_u̻`^Uopd;(:ߎNY;vGPlP/gcqY!~B!7,C/R\_ȭviₗymZ[2ln |1߁ٽ5q]/OGrhcJ O+v{k'l{5]_k.5q~@' Zݺ зDFܘ,GWi}WrW=(2 k&N}F)eyڑ=!Ѐ]ťzQϴ:/t5_=&E0d|0v>M7Fm6!wWSЂ$oq_Tsc=)jFs ޱwڸ%-Zr7VŃByS:A? )i@r Zڠ=Zʹ|(*  )Su8u]m9Nc5ߘr3R(=l]m?nST{^㵬}5OoNLQ Y~`eUqe%?|4EUxNB/qתc=ƫ4).nެ}3⮔ؤ.ՊQHݒ?6sny;dsq})_<:|\y[pæd\_^j/'s{.a̰3=>=zm1rR,,:<;/ƑqbJC7x!?+ l?~ q8Ou:/F9_y*8 >v9s\ jy;eyApi~~?#ugWczV6T>[m%ӆ~XK&g)|̈xu8Q;~fV"gM3I& uWܚ4KyTvsjdkZͿkZXen^xwC쥨.6Q~]FyBҖj7[d}:.>gﶠh.zX;&Gٞ9Nq8_(xV*F=CS۾ܠO3?֢eU zsUQ^QEu_~I?\LG&6(}7$}Oc*b]/S ?ۺRn]Ww, ']Ł%Mz:rqy6[y=UG ~_ݼ2S_n^_&W/_K3H7 ~[X^d|^ ][NJqQohQq4UlH]u^E2ou:y1j_j"g.x?]'vQbEU\~qx)uSfSD›SոEO= ت]=\ ST}֍[~%yM1uG/hrU_>d̓a)|Iyd^`f=m}GG)΃S֥S]埌Oz|UrV2ƒ_ڔ,>\?0jfPo¶۾Sȋ:vljJa2L_?2tzXg#8wkBT s %}^s?ϯzGƧzX~iH~NT: +0gzT͹wҥ߭p%FPrg.uԦ=nwwhtǔS5(^WSBbI_̽ەgmqӶ;lBɕݓH_ȵ}8==M){l&q_mHÓg5qU*~4n?۸'.{yˍͪsYun˜7pM{ 핻pv}B![mLX>~nV{:]2OufuIzA۱ZemY-̦|MWy^N;>,hă݆ cUgmN6;|Cg9CWM\)ӆP^>r)r؁yAF_|rx{swoI~~ \h3%Ժ0x. W5$vXꭵb(=2)lr~Ϟ>pۥڔrl)iS`x7jݱ=*]H7a^%bq7vŽ]yn8-lo΂ڸ )}WHJ|o~Xr\^S?K[)%Wćm_ߵ+׌NKU$ɟg;M=e]5{vd.&Rۯs=A>WgO:@>~pFy{}o;(0»q+NJw6֞{6n@P쭒4E)nI9>?ўWׯɵbjVl-/O%>pb7Uv dm?u~QB~#M{z8Ye)RWِľϴq!*vhCq_2L>_~-۞b2kj52o,{\3 Zu:ȍG(|R߁fqr1QuM(np=!Ϫ#ozmZbO4w:\ӄ 0DNKOLy}V+bt- " eyuzS.u?y`ZЇm)zԺq: WxD+j]Tz_nImlNѼgzST} ácנ6[_P=]N;ɱuRqkO:ޛ6%Ej VS:\9Nب~ s.ݲBT\ sW E<螇M/FW߿랱Z)P&=4t.j{wuQqvN0f#vVuA~sܧƟDuy;bEgaj8UX;ύ`78n'˜C!mjޛ:bWt5Z i.=md|*KސOar\Ku䋿gr,)tQ5 TDkOoox3511%vfڸZW\}iߥ-r׭lRϷU뵋D jv-iYeո / C!7Vsٴ\K<éҽ^t$vT`>\}.>~߮6elvoߤ Ͼ_bow 4L/9I7Kjn{5.2iJ T)-s/?畴i|O=CoG9ѽLR PGuG|.Ny: w-##otZkzv]3AՑ;Z/ D ԼY;'_TO UNOhE!êp~o~]olQusvBΩٻa?,7QHj4Uqs#m Y_xm.G߸c7Щڸ$yrѱX}_zPRt H7εub@gNQ%r6OW^{~ٕw~wsupoꖅ yUd)ٚ\T2n7N{3۷q3l3i8g#6zu #Y7¡l:Wq~SW79EFYeNIwyq8wҮݓlpù76hss4q>>/6 K k[FGwQo&;4ׇQՁ%?^1V6]l%lJK dxdm=7!G I-G 2]ZpZ'ǿ&U]N;]rĜWp~xHWc̵q}Й +8~G\|36yi2xpCU}cYwv>˷-5I:ӊ?Wb}Iӭq rӟF4ۚ8Z'\m4wn M>ZlL Xg]J7k[*Fu|6?!R|'ym iR,._UO$?~~|?PSJ܅)v }S(+1of)~DNUJWѮ彼)~ucIWgF~WFv}~Tvo;4u8ymQsr'6.Ks׭l>by :=#sxy}xo/Uj^JVez?׹7qQOFzju1:o}K4E)x*fdh~`xi/[%Vv`;5I;3qsKT/UY]}<߽cTRm{#zKcCGrGy 7_.8O;9$aUi1ӺYSXK /,٭$+پԎrtx&p`k9mNM]b}9(giC&[O+k8lܳkx)O(xuPf(]8%|yI> <$WfmI3nu6zt:}8žbI}}s^_:*o}sPXgwjS#K;>;ןh(  [{dInq/QKIp/[[>i(ukW4xD=]{}g?j\Xw]duRroǚ8My0݊ω[:|{X1]|uFÌ6MA.abkFX=#(iۗɧ[ߴϷdـ(c!j {R`JhUl364%ʹPYɹdg͸m6k߳5Ki]*.,w-C)_u*=66=]UM֩ MEO1b4q!|g;fOMvx7zt}Y~r%)IQqtHq`5/&u GGGHQ1yy~ߧGw|Gw3i- Z> ̚KQztRܔM`mWS#2vϯ(ƾ3|棃u"~͜Mp?֫QqX=Hw0⡉)tC_ց_u/MP#u"!CDNu;xyu~>hϑrjz)Q܆GVUL7.YuRX 9^uC՟|߯x\7K)ĶÕ-n.SXVxπK(ܲ|끖V n¡/)hфg#q]S,Meo7!vI\ O %׆fj:V)\gNw/.ly6m'Js\^$̡N_yG>ƃ(!TК7~&B.{RBz_XIASvnpD~cOqV7%Y^5EiOCG;D/<6̡XȦB(PwikvnfeEݷd8l(f%al +dtJ_fm{TKE(2eySHK~-3_>`Vޏ'La7?YCG,C_S%sXzkV«Ќc*w8}[\c+Z~-POn p{/'lkOJZݺ W@AGGW6eǰo]3ǡn6nᦝw~[_l7T%"r|OOIoLg7Lo)>1%L(hKwi_߮Po ݄w?q'z ro#j?z ]#m ==K71`FkV)U_G߭Ӑe1YjEsShjI5'i㾹 "P(d'M\_5ֿOQ#z/7?I{W+ (6Oq>Ww"; %Q(E27}񩃡v=coXUzugxUCS̢@ߤjpnރPtӨm#E 8LiaTeQ{ :Bnhߜ [Uk_55׼~Y_~w~.^ڡ' 2y]C2|x:5:8Ԅ_֥K?#4!6FǯterKZ.JngQuKVsn={*Ql~V WV!';/[JŽ^kA>_9U2)=%DnqPgwTz=FuêܵonxgJN54bc͋M)FþZr#_SЭp-)#̲j}Dױ]C-~ݩp@=Q}_ƁF8~]z5VtkU?mDѵQ95yuP"%|* }Pz !?Lk^?I;20&}dHK B'Rެx$jֹ G75KQ% NeZzW0פ__nnӭ~OAWǻhOS)~]9^n&iOT]ˀ BTۭ-yqcrS.]{Uw;p]u6f!C 3L7P3khY H atWX`XO[M1:lܑB'7ouuAגǃB~Aw*T])%aG!^{|[Nm> ON ݬG!'W+g=Jvf횧 xE)Umom-NgyJ_`I)I"H_B󍇹&G< %.t&;S3㣓_.|+oGv6߽fZU\Rۉ[ӏQ܏]Sj5@(S>Uޑi]}n<6]2>m;քQJw}[Z+:SGQ[gnq_MipSҁKu[{󬚝-6o?5J{G&+ט]Ҽ4=9]Mc]Rҝ1;8\jw:۟xAJ:f10Pܧب+%Wb(oi:MrLw7p׸m6k߳le\/v)mk^_NGUO]w6,3sMm:EJ=,Q1 626n.ǛZS A  ?af_o÷sdY?fio2s0?Vqz}ݣ(vWեjK77 nQ)P`s;.hG녋燜"ÒSV$}WryӧilHU7mO݃w׮s0_z풔8vsQSk}uc km)`ӊil6d7%NiQv]נ͵ϡs_+S|zOWzN~ܭK̭ JXz~G];ĭ uUjt`~kҸv /쏌ʵKrH=2|99!?J;@0#nSI(޸|Mҵvz%Q|׷W}&qi|;t(R,Nh%V ųCGyjWc4wBwNttږ=^)j;V #tV nuZ!0׬zΖ h{ce5?F&Qԍ$3SЯ\]뺭>wEՌxZNgл}8-KmG1./iBF||^1ESB]|.ٹVUvm&PFs@{$Z>(x{mmv UQ)+u=ֲ]`Lݻjp#GfWQa{8 <0a巤6R,!>FL6vo?C;vO!ei~Kuq֛]Q֔6+ƹ}ʚ~Qu/_o߂ 쮮a}z;ʽw;&15Lե4{7Fz [7'rs{u< ۉK)vմ5g O_˼7a(z߲W<9"+1|z؇ ƶڙ[`:m"n*EwتQ4C#yau2T)u~1A W008K}-R峧&~-C?شF_]w!]=UAыώEO Xs <|:bޜ{7noP;i:l* 9mz_Oe8ސ\y =xTmQկg`U:Ѫa =,Eju^^͂jm- 秅܂z>QS5C;'Yt&|p-gЅt˶=W{V p 'Ņ?;:n lTȉ laWj>GO}qZ~q{anGAٸʤob?>m˓fjmRΓZNez_do ^L.;{֕I>v ׹zxo&1sL~/Q 6./$R'\T>?+vt<\/Eru:u/5;R!bΨʻMT>JhqkTI!#{g~KS;$x;ؤZߡL/7Ua' Ntb{y9Y}YnY{\m *v_{כU kp_e]os +f`=DU7^u>jr£PPt+^WA¹墠eVA;GtŁ)bKZ%[z.w/RG]-h<u6KqNZF3֫ yǻ!l,g`*f&V;N_Kc-Y2!l#]Bc7p҈tW6ΗpYh3>6~U,l[{aeJ lYmq^R[%8{ͳ2Ԓt6ƹY8LI.Қt%ѝvl}D}3_)~ 2>+vVb y޿nrkv+.+.+.NCV\V\V\V8YqYqYqYuY۬o)R6(y7#GӮr'P͊ʺ%2>ԯvtPҦ65(v0NXv=͐J/t,~̝N;n)J;7٤-r})5=d`Q/<@+ ?+ܟhuN{3oiutk1/_ݯ?nӇ SG`Uo"˳x)=wEfd0J:4]ܪc^i+rnW%X𫙨Ekn:^q4C25"ܪEJE3kh;о咮jCȜ7U)1Qf[]G:OO)_Pgg1y{`3 #&b2?_H7hgƯ:;*K ?iWNoW[yc(zω/yob_x[5#[_1eEnRf^ewxkbjYd7}MqG&Qܼg\(|j]&WN^U|yFd?RqNlzwېQlg׽!-W^_ vV*~_ζM)wEUv0.zU)jr/y6&_cs՞'Ͻu+֜"/}q: u+xeBGǛ5ơ߭vk>tuWwx&~BOZ[KS]\Ӽ)]uIjٞ?ֻWK(woq#d;{Gv0sy85.^Ww >Yl|j{VG%|[T'/yuqxo?ڻo{/T v'uy"ߢv[Kǫ3xχGLƆV z:{%U{>sv7Llɥ܍);;Wn?Diyscn5G}N,i1%:d*|/ %^=#R=] Mo3)1,Rg뺩xYw1榤;J;{8[0xƊOؕ⤝yg;*J;_~`<?ϲ<=g|}nMBPʟd]5nAJ: >e;n;tVz!~Or?fbm-CkuO g. zaFV&tN6fm{4]Puy{Lߣd?[~?=.Zˍ(hZ;Q?.]WU~纏()=x}Rw2vSGA;upQhA=0*FQ?V1iy?&ucHoݸ߭OZ'ZW=Qc-K6JZҾ<:#-JY.K`{K :jjO2?&.r(`f3zկhߨ߮K8HӅvQ5i_^{c;,^w9}?Sԏ|G7=d{?=>~N?=w%シ2o|8\vnlC|tm6k߳i ׸YX߮: ߮O;W(f+DQ9 [ӐB+vߌgQh1o+#?Ï2kx$?&ΣR+E1s{f~7*>lWnM>P]OExcPr8m~ntuq1'|-NӾ(Q(gc/kKeb^s)~'exs-Më{m ,|`cynlE &-N$11ѿI[LWkCenbBr+(4l?ƻf$@GKk!?r~ݵqP+nlR;; OY~y(Vy f];w7J&Ʌ Qf=VhǑh:)boʝ(05_ ggxϧ؛k*8x vA7]WRQJِhӛQ#/!m:yQL,Uu߮O9QE1J=*s> ,Ӫ:goyD:qc=R);$;E&Ek0PXX'?߳8ϸfdlPo;a6Kt}U=G?S둇}y|ׇj󍥛əy;tȣ[S`3ä&FZQ:]|躯s)H5WcsR.dם woWЫjݫt*-غb;kWZXԹzZݤ76Z'm8 FVZF]z ~dz5ֳI}j~rdC3SԶ`Y<Uqi VAUP"×>c3Жbxviw ,HV~浹t\%2tm2*8Z;?rȐgӈ=UmZ {=WE!k݂y6Ѝj^sЉ_/TtI-K5'(wEXNw.t_RPɁKw2Q|sve ꎳz=-b5)VnQD}y>`^iq66NN\Kͣt80m>{6xp07ZDyvvA7Ml_%kH~VW/%7VOfk=I#,*pUqcCD'qm/64#yT\ t1kȿҧn5+n˭J$z4'\UnKWE%ٽkH~Sᖧ&wgPj|KպcI>N&"3u|[uqM?> L!CjGS }ƍ8Ąf7{@!#r^mcڸ,ql ~) ^rj^KɽT҄i6[;wb⮸lFI1O-5rB M r՞1j޶ݞLRqĿHv*˵qߔVuL2744}rmWџI~wvo궫Hoz'd.=rRJk)ӤYxm6]bڎOjn7s}5<Q6W-.1K>ݬMI]Wv3SC]G^CIc\ʘ(k|27%(>cy?=m]{؂_2k#mPyE^j!lnd*ʮ:x>(u#s);wƵf-=Jon~}`z|o=Twz1M\@:6KA䜹w=_Nmܩ=C <f9Xf&.fRBUܾ` v]2OIGۻ~D~+FM/P~n]k%<\B䳥qVZv)qmXUJrgYP(+^fS9ڸvs 1 bNqœ{6X7Д|g_y!CMON#( 8B!?\.Ƽv޶O=*,ĺG.fKٟ檉>hۯ-Uwsj7o*";NTvrk5qs(;w8 ],}jnD_4yjzvV}JjP>>EO֢Xї#cķ~y дv7'y&D oUxɗ?ybW[]Pn&.(X.uХOAWҥzyq~n,Pl{ոEKB6T B+ۦ^׬]ю15;ơDY/vBdg!7NW[{PؚuRGn?ajǸF@^.=Z^_ͼٵ})zM*玫qn7ҳ:>ŏ>C:Z^'5$xOэ%5825SFS.ǿuv\j'mZx0 4sH![&ah2f^3U"8njS:(H1c\,4sj [yycCvCQl+mLP/L{mVPI-&a;W ˏ_fk6$S3y,rA KVH|gzPru5j0}LfPww\SL5ɉ81EfZR?[g9|/b_wKR{gnazJ1Dے+yU??POx9I9{IT s2K 9ؑ;nDzifm"E{+ 㞰jRpRt ?Ok AΫ׺gbs4GaB]jf #ZV˭תkֶd( ?Ԕ"O*8sYecO+?9ꏺDxVE5SkҞȋ]  ?M;f`|xBՋIY81na_1sUdu)vEr_'vKw/$w|#6g9v9xoֶդ>R\{<֏{pz$8T>t=9HdgW^|Ӷs8zn5*۳ڥW;_>IwYFoMuGH45ɋI:%ZQ4Ͻ8fqrGfEzeٗR58y9^,}Wy!Ax>&]Ƣkju߬ӐA7|I_ T]lRN/L-cP/90nvfsaВ{| P oםȷPF 绪eK{Fi#sw@/0c&/t|]_;=QfqA+ WYE oڎ,"FωMNF %,lO^lw}K̤o]-g|Ro[kPBյ]Gg<],Ȧ SJ87.K|G4d[i]cv[T(1p<0SIJ~(B::NEh03G; _pҜ)iHWGSPn4qwcyُ66n񑾳  {}쏍n+X/)e=óe gߴum|7;`]z5Bcz ok1_7s=}kjy}Wߋ|M>K)m}ۑ,8\J9OIf ߥ鮭}v9)SȧA ɱrޭܶkvmOIi~Zw=y?$/uBp$~L!Svݺ %nIm:m mgt0`%4i"DUV]~3e}Yz3Z YdgTNUթ#قPx՗fq6m?aOom-67KES>aUܲ˶PHqlÌ3;'7^չM=MKVm[ tŪ;gr|V5~K[R5!Pq9B3o'~XE :b:w Wqۖx3όo?sç6G}Ÿ:D!nUp9Nη.bU+c1^~&.B3 )^o.Fܻ{_{˞J1Ҋ| tB:ʵII[#[UDMg!!z'Jsm̡ Qu_'&gXb=)x-ytyDۋMfv/vv}߮01c)bU֫7~AwmP76Uy59s{gO^}{neI9N=C+x6A{<|:b,wM'rM-:dbNvz[ k1gmY- 1T#]hù)hيyڍԲ}ZSo>?OůU{]-hSSlϾN:N<>VaC\w#]c6]ϴo\¨KRNV}H6M\^wtIC v¶3w5]_kumXt=l|W]뢍[|]7NR E|.bv=ЉŖNKAcwvQ6䥩yGO9n]W)9&؉KP|^ 8A؁Jj|,4Xq?.KK]=?{?h>Uˌ vY{ҲIq%bL[fz⍶3=ŕkBM#&LYu]{ݬzl)qjڸ߭fuYM(twhgM NQ*3 '<|볏PTbkZFSҗVggk:&E~2: Y2TS@"̳9B֨տig(d|*,(͊Buwy+˕fce[5vq0ݿyC&qA7ԍ)~/OA2<ڿll!R j04v>vCl5VPXXUYONC6ڡ&jݜ^mL.u] Rܧ*mƘ~8MovTgntR˯TGM뾰W&LkG1_$ٮ}s~jHR>vwP̺-I>_ݽ/?aPwʩFjT#vB1n'mhX{ QL Wס6[j9姌Nhߏ&Ie_ Wvśd9P}~_Ǖ*+Í'o0%:sErϬLqKۅ»6\z.tk;++-AٽN|%èJ8xbH¿]7+wnccOʪ#VF&F^9S-E_Ϲ]V[Vj_hV"&ˮCT?o^VǛa})\L_;e{Knqz[^#Ew*v x-UT:vdzOy52:sx1ƩO^c7 Ŗv)zflHh>`o1ɪԭpU eގk/'4{- jPKcck q7Vn@U'7sYqGEoww˲7fs1VYK3v?_AAjRo*ãܪVnL[V+WR=]X΃u#vȣ^g|Ry[xUoUt)B8T6Vyw6R;q$bO [qZ'o^ߘi:4N!6\F+)J8e\^N4>mvXe:Pi>SSS*XQG79};<_x۵'N&Ook;c*?vzgux9}He:ݺ 971z-m~8Oe}NCTz3jh##`[sg'(9iWAʤ?bs11,O:}A͵u{oS/R֙7l^Z~}vJQ⺅Fv&G#<^y֡˳:x~;3mGEu} eNwr%'GVsv||FQJfY2v]'7`ت)~-ϑﵱ&vEQI1jq}h%/ˑt6W{߬RJu,F qw-O?x i.o It橔T&,"~2wV=t(6$b#7t/_رvk;;HSOvӥ)ޫ)iJ>o.uz˵qVK]kKIl\Aӏfm{T>>=/ZQW}GYTmJX$MW|:gv@_w[ 8na=yDq'6Tk'I¹azl/էtq$w>x$}U2>x"@;<.OU7R|*M&:,ߡs+ŗj3?\;o6@F.m)YwG緓RwjPD=,)>Oc/'تq(acʨ>A=ŪO)j٘'7Ȑm{ {^G|/(:e[ VvRuNJ6w])𢅝GaawQ;t H,[o7R䂿H=_iV`WwUөj|L@U㾐%9rz^6H#ojGF%^|#v{StSQdzܿn+uR~EpM@_忾Ixs h+v>w3zT@4YbX'>-=Ig/6[׷%5[PFM^L=/mb]Өoո@'2姮;3 \R׫z\a0wPLV,=gFZDttp.NQb7fw1apjnsStQkU#Kܜ 5-_RGR#{vIjfDk]K[x {pi#}Ry| XE:OuŃVJ Z>\9^/c*@"捶R*?Eo~ g.^:T2[(E?af1̝+? m-:ssYx8Yj4u^>{lF`vտj߮}):͙R׌Qަud )v՟d]xj^|AReiտعscO_8vmJn}:wgkPMW56 u6uaϕؾnCƒE 8#ˉ^tч]'}[fynˋZ\Hlc=G<7saZVsr5|'$>s PAkS'Lܼ~pH0R/SI'_GWA}>IazWH +Wo/?pTWNuGWgPn{^O13شkU ֽ9hU~+Q+\tBZZ^;n]#gm(dmWqS~}%{mJЙgV5u T\p q㠕:\.yEN,ir/{GTLꪞkNwV4<|֞iL}κ/mYo۩xeTE{|}%Ɉ4lyTܻS J>/hur: u&>8SܬNW~K TY߷q+mQGу(ܠv }(pP~{%J]lYm2t[Z6.wŗʇ=Kv=nr"q TJ[U-88]9 FZ4(w|o#y2ތKP0b+Z{Ћ8X>n2Dmhg/'ٜtёK֍M txc3_~*f/Ĺn7tgԾ/HQ͵nrQ?鴻-%7)?d3Iܜq7rE|!%/-۞/|:,{V6t5G?Sr./]J~43Sg]߂7?Q©s=&|^qcXȿK^j~5_vYGyfVtSSW|^5)ѥQ6cZƵE|l]7z!%Do+?S@mr4;emYf˖+Gg˕-o6263;wlZ'#didqE~w? {E~; op!        a#2lDȰ6"Fd؈ a#2lDȰ6"Fd؈ a#2lDȰ6"Fd؈ c#rlDȱ96"F؈c#rlDȱ96"F؈c#rlDȱ96"F؈c#JlD(%6F؈Qb#JlD(%6F؈Qb#JlD(%6F؈Qb#JlD(6F؈Q`# lD(6F؈Q`# lD(6F؈Q`# lD00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  00L,  0pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL, 1pL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"1HL,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,"0L,=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@?>[ٲ`ƿٳe+"c}sGϞ8~eQ([vyW؝ؚU,GԲ8ח3\YVȔ,S_޷Nkw՟ozlJ,0C ~`9fZm]pqv`zQK<[wNwBs۾ z|?wzM\e{&V,П 8L"Wz5ma'iלDfnfR7ĵjuyGCkjUgScګr>9QNNm&p&d1iP]332[V=W߲rBbiEN>"jř=R%v@ET?P^eN.ί:lA=^^0$sŲ-R vǮy='wv]U 3xF.m47qlL}[ruFM7#K,;|_ծAN>ZpۆCȖ:^݋| yXoL=+: wO ڔy'\lO2+㻱zHyԫK%3-#zK }Co\wB'oT9|F=of݄zX ={ʱMm6f?z\XzZ\yJ8ׄ[;vRK.&.:vev% Q^]GRJWZ ǫǗhWu˾c]/˨ϟl,]VݾW-K1x:5陻ur۽q}v{>ߴ)}K ߒ/<4V8/L}hY+9[7z^`Z<4rN.#7U%/9cVX"9z?H݆t [ݏn7MǵC,IT]qLu23>T\zwPZ3ޭ{LǑ.SKlՙϞQUv_twwwmR@,JZDD1EDAPJ,@EED@A9x{|:??qk]sUγNy@TZ0s!z̨?N@'Ѹ|fUtF/n{(s!ڷowc1#w:69n j /`|aD_b|v!!/1/A޹~ĀnhTuK^;\ ϭH2w\,&䝔G^Eٟ\'Q3jf3۫Y:X@u] u EhdK<>a 6RnJ7S>qnY{&ZFKG6@^kwksrFۨz2 : >'}'gQ44먭d'l=@E}Xp݅k qצ Ox̥xvƂ.k{'hֿmF%D۝[C hՊp\D=ڕco0;h|vxk霁FVǕPu'%Q=FūAȩw}-o0y:#r8pY9'cLy3g-xVOw7,CiYji%K}-g 0)KH}_yu- `tږ .~zVX͓w~:r%"e=]'7'sK$z5lIHyWTW &VS7DEG˘ ZFEX\i E{0:%腝hzlՕDl_m`8ӅxUQ:i24!F|_cS͌qZM*p vtEgadll 1ǣ ux~$E Ap A?&\}H LmW08Ng?'u_>B+xʨ\oapBO^s (y@ٰI$ RzAv)IZƶ/e҃0]+u>Jy8#]҆18a1F L Zы)&jI&]\!<|?T;8wVM=)`eU NdThgl,18>V)~ CLZ8ΐp[iP -I rMwjzjxRH>\кEoDaptn.~: ڋQߝ@ͨEۿZs=χ£vB6cm0ݶ a>`  hE@"V^k;8P0 eR~ͅid=_;FЖ~N7h)<G&Uh,R]~3dov+NfݠbB[3a)ڕp$qIL6}@w ɠ1xp>i_i 'ܘAtf:9UVM:nΚ NS! |9٣"*]i,N9P}"o 5L\"{ neI?'?`:~'{%,ɴ ϩ/9U/{cMLO7{+E 0yPLqIț2̿8x ChuW T)Ƹ}o&x Pm.Y'ȝ_P eII]~{-x)Ӏ:l@_EH"N֠-~!ߑh|?}O) 9ڍO!:8Ws%ıK&o@x6{"q;D7,D A!p|f  ޒ +oɀ4ʧ TS%{X|O s$kPL_-;qE"%5Y9j,`0>8Ya#TS~En5H-vjUM{>)%EII25e$_ꦃ;Lc]fC;Qnfv$v+:U=/Ġ{v A/NyXĄδ!!oId (wSɣ+gh}}>sLDz/1̓b=uxP=,crB+f$3zsEnhsp*nI:F }H _sHG߯ wchi|9BՁ槳LkO: v?vᑝXx۵!N5rcpLg@@5H45?Nn\}| GP'p/͖(~vKK"Nq7Ų*R#_'U1 =2~9J2.9uEa&(R'o'4^anQ_ čwcp"jV@o1MIc4F\~-6cp,ZW/M(_ 0/cyfX Y~nXg{ݮ=XVRG`'"Z߇_'Az1xd718$WA%BJχ<6~{SAYCuR08&0z d ro%hX6<^TG,^DݤAÀC$ 84H7͂]j?ẑ9,;{šAVexg2UwO-zIa =Qʝy7 lӿN t)%k kسId{.^ֈǟw'{ݒ)`'N;l,(\' 4d`p*>aM`r1H >y;(bpi_AC2$<>Jr s4.׍"vsg 3eBw`ʓV=ˎA*oGȉE N!Z~)A0[W08#OJR6$ f({LR`Ezqe ‹~>+a~(m,$śp ԁxP 2{h8 ucO>[T2C~} §0P}HԳv`ȭۏ4;|*S&5:(XAPRS6OTcXīziB:_KwͱFw%0C{} N,cyV t?,i1嚟v7ݲ0:H^:|%KJo9eS̩M NO)zk28d%I{7C]SuL(95ƜiP{}߾80 =;>y,BЏ^5x=>zDzXkBp [lX#zcQR Aʍ{C(X'߇ ܜ hyv[(n }7:\xƽgc<nY{B|eMK3 Q25byaHG&tF~3߃8+.T_7{ j7`_;r)KO<&dr,{Z:~ ^AUy޶'uyѮǼqD=[FÏ~`eDN˶Y='8N-3c]*HOyķ u__HLjY0Ro!r^t=}Y1ꕇ># P{G {`Pl;_!/wL%:+u^4plŅ=Ѩmehx|}EY%@&,"}˵_wWA?~l͗b hӻcKC ʑVZmѽA=+}kޜz !K9E%u扮5ݼW,0\y3GS&N~+i `! Ny0uJ9lY`#MZ۵4{x* RaZŷOM R"/08ڠl 6 kF?=tWAw/πGs] 18$'8Ajf(N];:/G-dj08UY# ,7aA l}_5epB;P|Kj~E_\w[<Aۙ; ik ):Wb}b{Ki^ꕈXV^yfmHƣ!fv7 A}k;^:9bb^/,` kL_@5]J;v=llOsLǍ%C5{R;XH:8cmbyjދ_)X%ѣvƒ$#sM9)ؚf`4`L^k g NN>W0WHYp>/. g5B]FM`C$' ߈ik*7f}s \Ԍ gl*wzʶ!X([{fÙT`;K1.D2TK5}X<᝻X Gvˎޤ+JԧA˵!18hǣAU-3 ~gI8^4 z{<@ɠD NG>e 꾵oi16ê`Yg\-1]L 7F\,EKM.p2Toqio/6u)3p16A>3L֖pNz0q3fza;VM.zTX0 &Kb f)+NxVX Edqxvp(٫}"}\"#^ԎMٝĄ ,ODd ̣^s ~;Eߵfy b |#G\ T)H  =!!"E>oߑDfZt<4:5g|2$K@<#gDȷ'TJ*-Fy$:1qNf~fbt L3Ɖ/~Rqɗ$*dB\ Q8'A4/~ܽl _sU wkKȐ҃BGyg`(W&|Wb~%[-UR" K/*}"ݐ`CvR;t*D"zI_A®ts,ǷOtcI2m^RYv½Av7Tk\'wzZcU!' ( V%7ρIOE`*|O֎jy9]hL|x-G˯FAAL_!>Hk>4^Ig|(y(NdB+j'I~BY1SrG<9Oc.}bKwdү*hg#jE $vH,Dt-/|L<x$ nxS]Jj/E To$PI2ΣLC{UC6 p#x v#ݭV{:cpJƁ6 d3my/?&u"* ]+[6Z?hN `h>k=>>R Lɷ}G7sznpͿT+>O_߸9C'|(D31I;s60+?FC>`ɯ.pu[:7҅f [gK̋F18zL`>C\Z|18q3|bU0~\w9h(h{6pb0a[|,(HY몯;cp!SqP\DBGl &K 1k;{KY@BW_u>}}XĮ#Q\19m-)Ǭ'?ηz\:7:Kcnwv )`j* ZGnPA|yƓcYl^Ѽr5# O룩A/*w18 (8X{jv:KH; kgO40Eة2~]YDoC4u'Ok˧}џM$sSOzDK΁ʽL`}$FTM|Z;<frѾ rA+Ə%s,J\|*orM^>v&H}McQڥDd61/Y6~0e!eNB XOk_NY󋙯KD l=ȟ[$svf팓X6WpOށ 9SW5m-5sƝAVJsϋ NkLT{3voY[<}S{ ހҾnT?Ak< NB^l,69V)?@l`S}WptmgqlJ'?T=^Η*y,ZʧdbǁZk޻`sGu9J^as`6K[\bJ#Kۿv_x3gc-ڇ_rk# 2Тy{ymL,r[S6X]np)c0-ΟiSGam =)-&*V Hw̧Ƶo+h-:z"]" q',^.޷5;=3Uj= slet3!;T0a7+H߀qd}0壟L5{!Q O;蜪ua]O&W尭2(o>S`<۪JOHoAvJu!)G%DNx.3EF:X]2#KAD7CK;T;[=5\Q} b"kDz3'cAMaCNϕfdVH%afIJi{HaLS1w'hZgo?᳠Ng(&WYt3R3JsYP q!3uT;Vgm<6x$N2Q4hV4ye?-&#;;sAO9Grw >@ǑGNt-(/_*2)F|hy1eHGn: &\Nd@:FY^ ﬊/(!w5uH?96y4f$wevvx7 (%LZYXCO?L[P߁ڮ{ĉeVֈReb-:MK= 5oNceEgdNGרAg3D.'*|%vۂXV`m, d/Ez, W[, OkC'A7j*7z`Mwct)_Da!!6#@}(X/y/ b\RxѳnMO l#n{7 CJ G+r- +m7P&VǨ2GC_?-T%*e>e%GOLo.^A z"!WDmMh]ő7ThQg^'B -X‡z7+?$so@`-Q@<7W?zl/N8'#ޖ+Ψ'S[o`A}7JrEqh5hO#~h"^z*64%xx@*~c̾@IWnCMc7*U:,g")kf N# MnE/-%w/> :c'eAQ)͑q BOž8{=Dt"G<ctg"s\K Dwy^|ukG2~;ҭ.ddI]1b'Ike/U }pاkYoAqҷH^[,]ǟ/f:@'D'P%'īJ[ܑt./b8:όGf9XgV^}ow-=x c @Yp4˸TX_[w2) k46U \d]08V*Rмgx$;MGܗNjԦvn7 t2sq b!̃=wpҞpODJAi~v^OL;g$np (Xͦ"*jklA3plv9^[{?~aqne:h4NRZbӿN ŭoL1ƛR)X8(Ύ01Mag//43K-|uH'}J֖rsk׍կi2W}: fV˻nڣƄ@ciSD, 9k_ԑt>4jL9B vu}X;MO |`ĘaD J/54 W,AuH*L ־VgZљ`ogNТW>b_=}w~-P08 +r^캩ҾfojnWh˾4ŮךQLd,&3VM=Ud1\To;+=]&n/S})' qO nQ>y\AaDu%va h޴3Exq+WB.*Hǥ~1J3V;t[8m@#q)p yE)z G z? gL^]{2 =ƴhd\2sySS)vЫn{ :TVo73F~>@o!mMt G(j5J[U"{V #sr!]%?Bz$m!jAer";]MmRc6Y+?ۜCWzj..}E|#Cjo{un;bO&a%=dJ@Fu) ًԲ.kf@cwGԓL˫{ -$rQ^[՝ :ӤՏkKDZOo*EGm@LL*)k6<↶ >}gxj1`q ͨ4+be|9~ xs+c4\Dvi8 HoOd?n,+$xUݎxKOs ;^ nPU ͟t{м`f}ԸlRFy]:ÃhYJyp$RZ>"A>AѹU0h.T-0ލ#SEjvY;_ˤ0 D8D3֮a`q15M!lqa0QݧG )#9`pp!uo ?<ɎS̆}ЩSXe2='0~Q}(nl xק@5V1bqQ tne vhJőzolhoωk+@L-_w nR^ hebCSJ, )M෩'3fX$&N}1&*{N-ǴŽ{ [ReuDN'ea(-/TZ64D~Rwɠ)v{ŭӚ {X!ɇvQ=u4%JLAB%L~fr۠NK*1];[ ]sp(tgEWEK] r+#kG*h\SN~Lve-w.zOM\GJQ촘wdڂh AcKn SxjRݝNczϭP5] }1OzA8v!{@o¦5m _>Vƾ_̶pļs׊je0j+bՠᮣf7@qSIZ%[ޙ&}Țk"/2Aޠgcs:pF>X7h׷:CLxN$l:q~z@cǓc1@()7&MlFM"7W&@s *{҃$W}A|׆sZWUG ܒ+wuޙTd>i;x;a/gl0Vf,I#?4~fvGPqOAȩkkA8i8ӱ릖Kt!9^0koEEM8bPBb\&!OMEieN q!3(KY[#r¤iUs1@~H [@<ʪ㿼y{^Gn9 {zƇS]Er ,2'ɌƋ& ǼbsWޕj|:CJt|B3(فmLW#~Xu鏞Gk]=PU>@+( Px*-WbLGzT/7(Oa@ayopW~-A)fA,nEzEs=iO](هmH!f f(.϶ WuH0(ƕaO@{\h䧐rh/īw%V `Bڋ #m@͋êpoyp>dYU#ӴlI|^JnB`۝w;j4긻Ȑ o ѯ? E8;J )C@Dk (\Vs@AHCX?TΟCiP8 (X ; f6'ZxB~R=H_jWIwEmc.t B*&P U2wО'ܳl%BJY(WS,ӟ(&X,?;nyRHL^V0c {-K!~Lcѓ ,1TⲲJ*C::S@#ӘfwHf=@{vD뀮}~ӦCI\'аkQ$uaunrM;NeBp0K?jIG W%ݿq+_AI\]ǔGXrj>)qxy,vY;MANnus}u$w!d}ɛ3 Z%18p7/Ab{Υ!S]+drMW7lzb42*@bU ˴j=cp"ig-h%ÏQ@?8!{ZM&_D[+ ~牖afՑhjjƎihK+/njf' i0٫I՞`W+O9hwoE`^>Ս9NML ]Ǚ5}!/a_Σ~=:茓 b3Gq# Ϲ,!=zE_ <ڟٻew}{u/~U;ŖUNfy7/+#4L륎?6.`:ݎI'xW&ʪZh7P:oA~ˁ/}s 7=~qJ+s~*71?l쌅^@͊5 xY?#EYԌQ; Z G[=yV.T*ȿ!U`=8rGD +0CC0*|h ɑYDBqO= `jSMjR8)N`qK1t_Skj'CI~抾 rvֶ]7k,՚B.:EKsv NoRW4%[MkfudiCP:ѽǞ|8G -.-xir;A&A~(%^v S H1:o@.~P;x?+2>UX3$uwA/H~8Xan0)W~nwMo++lq=2q1`GPHds0r/$cdrzٌWR1.pSe[waV$*ND܎d}A@{+2n^yFu_ Pw ֟ ' VN>;WV=X7cr4/8ęS1♡&O'Nx?D_'dHU\#+HNG" ~-O@}R3 >y!)Yz"(bԏbXPQЗM-\"] [ >{V'(y:R bIBea80E/ Apua#NLy{ {ڃ`z}Y NRE6%(;H B{]f# Y(5 (%_{֞eaˣ;{o(I^Pg1vVӿ?'ɍHSnQԪ|w-̉-K"d&e`LsUp v_BœWi@7}5۩Ug'%sSGaJp%INXq~ŧ)ZWeCz/u'v?agvˏ*_ !X_% :O[iA Nz!Klc2 XE_h@Ph>jEtk0TuV+[_ȼ伓䶇#185ϟAFc ӎi6nɔ`,M97KNZJ,]"}hsjYI÷`s>٦ª։J55b`3_fn5W@7Uj:'T* L /,`w͟-G%!q- H(^E>쬇>< B7nh&o\D f_ߠyьڅ33v|5GG^ͤ!'2h5b]]H~A f'OF#ޒA_Y1()/etؼ&>v?\*AU.X ޫȫvh^gE:Gˤ,=CJKc`sz;CJR!9f7d|\]#x|? } y~cI/JWߎ;6HپGȞ T"?<N4ף:AN m{\vng\{nqKaO9wqsrwB<&)UIcF?p`wLF!Ei_iמ{ޏ&>7 s_+gJ9H4nґ}.Z,_F97ܓ.PE0V#F C?U_Q:8T}v Ʊ~/_xpGIv=Cc5mZL|}"z%EG{Gߌ_P׋x9os?ܭ@~ eq..rѸ"~_`M%%M="YCѴG<'I& p,q@wŇS}?c"p?ם}D})HS2(5CGR<6[n5q#3̕<4oʦSͱ#{QO@՘FƟ%/q? ǎO{ vA[.^kX%$B m+']Ѹ0?x1ͭ4e,${% fƭ}df qW.j|coDvJb!ΐ؈WA1Fp ᓔbqE|K2~To% QPe] q5m8mm_} :(`>8RN= ROv0gY+b,>z4OLqb 2_Rxj砸V:*sRz~w+2+@xn $ÎAd,ʪ}8A{&SrT U>l<\&' @i^FtJJZG#@[fd/)J}?n뎴G6d}~ dNE=GO&V_o߭'vﱓdUs$ P17MsR>&6EKG@]G(03,t%(4""`)WeӇ3}h7q,+ly \A`V"[apja!m9`),h4N#?N2`UʶLjwNTScpI6qt`6{5ya h.3YAU#/~'7T8xzASKa`uw?z9qܦ^`PTx;C:%:䇢=qӣMR4子K';h@~zf^W]SQ6%q c[Id{ctD[`2ZTMs~$aIn>ɻ`qT8K?h'{jߋO7w㦣+Ak)yٝp;ϯ\'NaXw'UQ1PzmS)m֔(\n]jpO=f&d]ꇱ Z It.T%qzhN x{ 䙈[$䅇]J nED7 Q *:q++o4Vt.`|V8/&kKkl")z܍x *%!ޯ"6 +yg&WXxW_ jeʋV7c.rG~9VHJL>ܢfL~njH_"4Z%Š,#3飉IP%<R0QF~&ZxA #s*]ϸH%m♋eczhY~g^IAS.dHyU' >k]ǥ  RhP#{xdHν  -L:mP'CR6GܼȮMH0gSHƢI,! UC/HѾԚ@$C%-!/9YA=Ⱦkx;^!~Rui4 A.;N6?ʯdh;@rEzF/jIC=UmQ/ M;qֹGEHw(z־4 7Yήj?V| 79Ċ>O\@۪>dp,LTk]pl5UOyC7 @k)+;72#8]R/S]-Uf-ޏZы< :oOI!L6hA|*m;RӰ=X J 0/agW\%%B^6%ڂYpwmzwqj+f7ApPa]Om:m>sdij=&{D)jFQTl$[E|rOylRܿ'"1s5BLs~V!v-ǓN3`6'&YoҾ@&͢%tʻ]9lr(渓{|,w.'f ak5ezMiAӜj1hztd_|*:1 лNwk'C9Zg 5vXu ]Iaops9[|~6` vI뗚)sO3>~ac%;u}ԁO{NR  |H$'oSqhOpCMK8ˮULUG`zhۤa^"?y}H@R BӱvB c/@%#X>|=YĂT 1-ǵo^ to_Swr/V -Am?(/Pzr ?2jayXJXm9w<oZrhʁ&ˮ5> WՕM9)*`C`Hm'fR>wd&}$xUZ%#'A7ڮ N}y djj[66^^q6 َ:>J,v=T` p:m&AeՋU i`5(kqPGi]]QY 弗 ӹDgw ,O^'֋V?xbs͚x`vG82HE6c#2s5h ^/j08{ާL|jԠTUs]voZƽt\+o2{B9h;JAW?ϥx$=fU UD`pJc|X{WI"ʱ:Kc&`#l3Ԙ]g2碟Żbo@SZ}ɜù)B/%`0m'q+`Sö tc|kV^uX7 48SbpOj]F% 3mƎōv`Ӎ+ 4FwfScprDr`7Xe ֶ<֏mkc`,ou|O;3Fd k@ #ZZN}7rKvwzē;+DYoɹa`OG8V=ٽOx6xz4{.7DJ [00 A~YnPu4|q̄hT*{5F/Onz|ZoO\y*[ SС/O.fa;F"?D;{X=࿨~ VS+DGr^^'(K&%G~]+ jOs3P%ķ1>x \;+s׋yT>޾)jm#<5z<"HO^gڕnTE4J >jV Z~wD4N~ɔyyijL'PW{3u!>̅|cô("dpr! 4y>El]׽@d0i RK#g G 3O0,J`1ݼlHJ|Ղx J/A7}j/£"%ǔ_e6 ̥+h`в[A<Ơ@Q_8QoR=홪,H1N ^+gtHHGqm W*{v aM?ƛP9Moqv>:y ih^j-1^xߋ< lԽiK_#s7qQ|X[}f͔չ+h<Q|C-յMغK^3R'+7kL6)wĂѹ/!:nk}MSb7.(TL=o?G^y8 :i@W[ `3@Ȅc=בJ^} B^/wZp5FJ k,q=0(Aq|ο݇ƋlKty=? h!qfu 11F~ASh^}XN 2O"gU 2vݱ Y)Qi Z)o&HPMz!Joևݗ*Z>eX "F6ePS3֞dGϥJ|)k!Օp?6_P0iPoEddk Z]7-W[qe\z8$]쾔>YayM\r :4?URG2Ȫ, 9l:>X=|i(qb%G=̲/m{e_NHjvEq$'8kZMv-~ ĦWzB%sm[I9'f`Dq9tK/'bux3Q6ɠyB*|] ;}98c77$VAFͰ w.~ÎS8f +JǾX>QUKD'HM;ӿN KK`M)y=g?P,t#Y'%A1'a}y2y 7k0")ib?@4z X>GWr35Dž F)Rvɰq^cGFѝ@}/3M2s=%rn:O-)j#՞Pf(>eG?Om6<2j39.ɰPWAh3l h EM];-ͽb6 #;b9ֳٍ4>ʿ}";N[]3Sw4ZAH3֏^ \$7k!jRv֟ 9)7ߍ)ܜظ&.= Ǧ0>S,nF5A~{{ ;sQ`8e/4z-$`U mQ|‰hP'Mp.<\}wrAg~SԴ l>\XPVK%EӋ,s5s+$@'YGBʄtL{ /~- +HW@nUě0ӛsSNx3/j#񈟴yLv!do΢3)K'dO}9+~K e/dFElQ?NރDzMI ϗ/g%/*~@tgq`鼈=_p~rR}+眃4pڡ=3˶P=FrЉnƕ Q0ǃpn/nK_ ήvy?v!^1-IW/1#<3o,?P |E^w{X;I< vDKOp! 㖊 "o1z#7,`L5!})]`lM`s"m3srv.@$L#MLTLqyu lw9-D=J| $w+] <Y|MK"'m 9yN1J+_ii9| ~~pl"N\uCoaWvs̴GW^ G,7XuP<$G罐7C뽹[L`UK0o[\OD:L_]} 5ÿxN"J R U7%(qq7_=F8e(@VXLXǗi(PG*T)_8zVP\l姚􎅂^++?=m ;.uV܃so3M0R/‚Eh OO~-ti8 i@ ^3?8 T7A{VgJfa;+9x>;!vJP>.Nu}1o%7@((fʥեaY?%k3evҾur挌 r"S r_Bqqm徍]`[pdNơTy``4G!J NB%'=?}g _sDZuv2TP) D^bHnTI.7NVOq<#$d9uLJ+/y)b l㌕X{Xb.ĝ͓9HӐ]9H)S X!DA B@:G-8ޏ5cIz ޜk */d`Y[AiJ~.QOѤ? b->5^Bm@u`qiy<]< fCߘcy33~{'b\@!u?cM=`  $VQn-10aoL8LYgF@R."; (.v- $`pk) '53[jeVG'XfJX'%q>tH/2g>EE0ϋ+HSOgӍf3D"d/U%+0_ywx7XFqsUJu}(f@jsUr{υo} hLݸt~VBr=88~:CxK6_0UÎSk*;sMpa"kOM^i"'#z;|{C%{~E|$/5}#728|8 i7ۀ3w鲨v*\{JЁ7u21*TFm}HWE$lhQHBv| wTr]sV`aҪrF!j1(NC(VsV/>{ _, .:f{9d__fQ3ķAm9{ 7*gZ8Up%vG+;>Xm=k$;V"NsCpj#=!GFőYt%K7%Ńd5ѭȯ#yܯh8o>y}~.9QEl_~\Bh V1tn@q- #/͐9 wd*B5;$|@O݌?N7vsqD}'}gR)2 ͬ&9 {BVʹcW?-W +dϑ!;d*CJ: b$], ;L  bDg\#]IK1jؼBaϭ]𺗮u,wKsh-!hptW?|5J8PҾ50~s84P`ـsŴ6"]jXj=(wqT#wVs"[ݢ._P KPMyK[Fi|8ctbFvdRoJ:U<݌eP;ŗ*@9+H(X7:Dƙ\)c°:D >l[t[j4oR:иFM>:GPRa7y샒?ί޵ -vv )f  Q@8 |Jl(bJ}DM ^dG> ap~z`"M9zaXOw 7&5;xoB?f{!!V#W3VAҵ x8 = pBd쥦fl!i6kȪ4uaȚ`F18r 21=Z|x{=&m ܶ*"IݷuhFZlR8>;aO~pvOz3=ԪHvjEsr?΅#rPk`_Q(/ s pMl`qSM)yrB?#XM|.WЪ!89d$W?`cSalY( .`Ȣƶvm:^A$%=?h쌵fv6(H]ti,cm"ŜAɺ&k*IyQ0Óq zkύV{O:_pb;〙$KdƁWb"R4W'E2ޫouc7MsRpJi>vPd]/,08˝ ,#]*8׫:~>]8^lB֟edcuwB_5!1jGYP %8e%6[ʞ4MЉrb/8Dl+NR`uF } Iu`2,; OHyt"~tob?{&3 a`jeI vڱ|}wfi0M=]vͭh~ޠثt))4Uí~{3`~6Q/NcusJbQEnP?:7]Ս9Sk>bO8pK4k 8% ˆu~0*tXJTm ]G "y X(~ W I47k0PVt |-\gmނU׸U-zryp:9͍ݧZ[v^yNN/CvļnRY _?i^AEp>2]C>whcq&|3&`q@ҙB0v{7Vz Φ`WP3uc:v+Sw5#wɢ3aYQ .=۲YE_"}z!>z +)V:[OS/m#BA7ϹWePĒll$tFR'(4b 0j!/~n7Gk[~J$~- $SvF;o1?U #1lRs|ew.Az;n\&`%K٤;DxR<>M;bH5kl|?x\[HA͸XW< ~% pƏ1ϧVL{Ĺc㠁-Q,obJԞ%K P۪. rB~y^/.׀hwQ["~3eyhAmؕc?f+VWAAn[>t Pй\~:44YHw/bxodUHgfs򋶥\6j]粀9*{Tه:z LgN6o5&]kŢU )Sn-oz"ܤ4Av=.y%PBjz?1dOnh~L aǑ5}躂0nVeKO Ll(sx!!ʐ2U&:"@l7˩xdDMU$Nt& PV_>J <Dz Ǫrp_CxnCڲCx 11d3E_;u%Ͳ_VB@3eW3>A|83`&t(]gvrn:>{SP-_E^{޷ՂZ 5S^pYB 08^P޸\PvvS%+7C@㙁B/h{KpL\zvY3Jr/Ď,ƫtNۓw\ϰv$.2*U.#w~>ĘEaEEd{&MaӠN%W@Er+ $<&цڨuʉYcp"U㝻XV,_j8F@Ru9H\b-U{m^Co`plkRhElp}Eҩt鲤9"T 7Gi7GA2,Ә7^M?7MsR0Vqhx;RC{g6A'Y'_b:NO6Jښ Pc=ul.{vguȭ Ӻ;T#Cf%I,4\-myʵ Wd9ޙB,=S:R]+ 厷 Ӭa6F XViPZߑxf&.:%;ޭn;.KP#*V"J}/{j)!1 i|~⧞>h4Rf撹z]Z\K$hŞ9Š΁=e?Aن[|ily{ -QPQoOTywVX~K?JO~zƯcu_ٝ)~ϯ^ِӆ p|#;Ix\ ,gHqx85X[< z>xD_EUr$:l¨俅ï]os' MW߱vº_ۮY[j܃Y;39S!|bxyp$|'Agp_˟:6 ~zdGFi*x ؋!:oRy֘$"] t&! f%}H/Ԝ8 e%'fқ H@Za KgcƁ"x`.ܘՏ\HG#n%sg(( 7v5vh?ۇw)cd)[=#vb4+"Lg{#P #OU<0o#R>gMH_Gg{qJa'{JgQe{-iIBj=zc)mY~,S;^z ކF3!O)JUPyXމAfԜ$?e<@(s%6ix3ĶOΧ$q5mø`|JtE^ӝ(!od8ړ'p ?0X2pc/B'"Sl=#ktiHF;[̼HzzZh?u@qQhBeW8IDr^F("%h12rO"w\> JOA/_HJb>҈932 5xYʩo߰'b<ވFFf`9t2ԮzD髑<hx 7Fq1Qw #H8^AL^3< F)Ӫ- | vh61@N,sxjrd@R]M|%|Lʉ>Ā:wc(6neчęAgg[*?cSл8BO8q<$eĵAk>X6ƿ' SAlB `vt Yr_քVj_yB<*5粹 'p;cp4j MGAfzuSE@C3.{mǢ8HYߓi#%N`HYI~*4K ȿXphk'8#0 ";iv 17Ӧ(,_c 8s[ 'Xf@oz;yɁ!cp:7 l\!rk7cwUP]n=pW'}禧ԼXǢ-^SJϫ |y}E3|1$1C:8DPUiT GuCg(Y"kв6%t;hOK46_W~aF)c+P?Uz۶)봝w܆}6}u};i28towGq#Gl\ֶ9ڿ2DL[Jƿ16liDO&Y޾@WXԾi?0l?:i J9=Fo~Ԃ8dZYvwjO|Oj(bS~޸>am{=3i95{zĄ =ZևK/|IYk 7Ϋ65D+.-M|lzj\0[َ'ej&S]qBzv.nhuS_Ϥs٩uufWwjў&?/]giS0`zl\nJ;Ǩ}O&2TCss\Ͷdz鷶 e6G!Y> G ^ć,_I]uJK[E⫯'wTߎr(]s9Լ]UV>&7c)8Öc((m˺/79MpVCpMoG[9w{ݎ dFasiUs,Tf[?iUFdrtT{.U}U׮VES݀Zz<#1;ʹ:ij#Ro~6&Rɇ&gk3unރ{xi%yݠ3iWєfu{]Q}Yq$>ΈFoT>/9ׯ6A^PҢf~!yE^}x0jKv' M;N"6E--](= ׽̼[S|_${~ӎzl^zT0%^ħ&B' pוgqlWDyr&sO7En#2ԴZW({S/yZuWmd.EҊ4.;,vۺy5?w)bdQtL#mjOnSع0~k}[ܕ"L |֕3ne^}y)ԵeEN&.rgJ_iކ:k6v',Z"vZ\|6v.~D(Qؕn#EzwRKz P=j[E~hAWZPE)`-5WtuiꑻzNdW? X ^!zϵ}6Fە6q]D~c_Ur|7х,0jiHfN{}3Yl@, o,z3N ~S-LZk@u~o7 }վ߻WϏ\JNTm)^A( kk;Ļj0ү^v_GTLvЧ N%>zKm(/kceRҘی<(dYo_ȧ}\5%z[qq|zuW'B[^&)t1"aoޡAp3SܓW΁_{hNn}jloꛜ6Yut.,4QD)v(L055-Iӷv R8{hHwK_ň}'e(Ug']֜uLUkK]m6P ͙"Lqw1jtg5 l]v}5~c^0/*}a:dv0{pkx+5b=gНZ~wj\-QU%_Wy7փ[L/NGQ\&zwB=o%mOY>?0o?UY]SM&NV͹4Ϩ2~lrh=ORvD9?_b{Ar ǧuƩzvZ~vM({+YTKݗa:UXPRCθ\AS-ho5,707Ufd(u6Z,i*ݲwfy7t${ʘFy^{|TJr_ػ >:>Ym蹩k:l"7*xlMaːEfͣr4l/oB1;.yD._E뾙oV[ /]߬IuW=ZUH>Ulft9nfyHãC-FI-:LY󒌇t;C+4v]+{9+k_{%3+Ms"0jG"= p+)ںtVV}?ITxSXl-2Ytz~N)#&v+:kw]#R{Ŀ׃M.oyc* ;LRv<4,;ݗµ}Ca_r+8lv_x&r4' +Ezj%mjiXgY&n{QDD4f)ҧt,~];t<ҨZ4a2[ܤs|M; ya(\nӶ뛆vju'6d%%|~7ٴXcfM1W?R,"Y^va0_ F喍Nl@´\ɠ5맰 :wyJ?}HkʥvS ++,lb5n}SJFϖ4;$Zg۽›םzn"+-NyV y-sMן~rkK~ޟV滪M\]. œ*^FŽ3a9eqZj<^]^z}o[e\QD.<!H)͹~ S|lU*kF)NWUݢOUsר7|@qs7PsCKO(qBMOԼਧWu/~efEU?9Lg*s# nDŽn˻]Ϋč /}2Kb-Wuqwgm6|o=`Zgߦ]oΗ ›$k^{YsU=<쇞-o[]Pp6^RrMMCG\DV/͢sCz/F]]+N듍Zo{Iΰq}ޢ* "UwpWOI}o Ttz_>~u>R3*&oY7,:s;P빙ћ*l1xri$]G"ո(&cf=zkeFwAnlGO/|#SFVr{>,_6z>'}-fl|HoNv,}vīunS꟣|3TRycVLn"Xڢ*!bD](;x,5PYloZ:IsM;ۺENkQg +GizA7 lsdxȯv.5tnqz,Wu״ XQsi>)F\?3^nKۭ׻dERVUjv/_a{A͟>ȶgvrB| ~'مH;A%aE9x`13UNvC>)٪GSpĠvqһ3+LB|"ǺN<uV;xnsO ;\=v? A͟aBAUvHNXW|ܮIuK/K^!oTeF)~z Us{O'=yیWw*Ȝ@^OU&yR/zwֽ0ή]3O1JַLp~脯_l(8Ь6.}qӢM-jZ'}ҵ%_H1iFȥ9ޒW,yb7!=ɩ_wV^'.}}<ֻeh}lj꧜og%s)Ua8Q Ugw*t~E3[VVVXٝLqu޴~{gԄ~e3)4STCOzX؍X^[h=":K_ͼ_U@+_CΥmd}5bץѾ~jUoBkqoռ9C+u7M5܋\ϟ;HZ_|u u^;*zt{ڷbcj͕/}k9j<žW8s/o×[@ѭj=F ϬW&7([rayöP7xfMJ0Yٔzleqv*v92}bCqצQ)OmujF7mQZ>w3HQ@졥~7䛣_KI][#_-JSeS+>tNޝ9ȭr-pGՍquՍ*Kf4ď eGUTϜaj&yߓy{.K"oYըIǞ8T9T*KnqO_ms~XƁ vЧ]ghIٰ\R+48W.7'WZez8=H[;\7Aɫ._E `_9^BjjYw\gUE3MP%{{KnKϭ={+S콮cjLT0^j]rG&)sJ]q厪cQ&z_ܶ7}sS67+R cs(ɮ&ygj&^_D>Z]9wi=6FSEò_{P̑vܼB)_7뵶}K[Ϋ#P؎v?h7ЯRS |9˗r2w*ݶ6RDQ+gN0hV=yTwk~9u|+٬0*7yuKٝB]%ǀ-^!uv ?7Ӹ)`l,Y~+2xٷ޸9}ide0-5̛x@s~=Յ,&k^^δMdci]ugqB'ڸ9Q"n#=:_+uf ([?E+*c-c\0;(=QlkLev}J\"6:=;\4Ь_S;Q쪪[u#QWu\]Gdzy+eG)NWགྷmlkigh׽)n^3s>73rHe~[Zhj4lv>qcNuDKfo/-к٦s*QmL/WdDQM;s&\؍\)]lx7ͰQvƓG =G?<"g, mxУmf ]j6鬦]_̢QY\1h׍O<;Z`$fr-;?jv$ç:^'sux#DEv g;O2)l/uGb+c)̟S-+ekyOug?_icnx&>i)u7oPu )l%ij%nXХ=GYPǓ׾Q^{KJ\vZ=67].UbƾsYh'鵮IvN(z=)Ӓ'ymgJ8OkvXdʗ4փ 8jyunm5ݠ/U}Nв:M\Gn})νrJtxz=J/0$bL,ōP볞Z/i+(|AU*mjcH9V=A՛y홻3m=׫0 'Eگ#佨@[Soђ :0ޯNꪒܪƑ٠^YY'}TvzU|AEr:1.5jSL?u /N3O^.@›rUW[Jډ쏳B_ݬ 5 ڏ7O.er sʞMG>i7-{D{%jhJʋve .pTv6\Lv ZZEs4{I#^4lXs WUǘ|t|O 3^>{5nZ86H9ӆ+{6Jk9ɷSkk۰Q(%w98 yav|#xym(qӴ%:)Wp ٵkЅrߍhˠk-)ErK( o q,TKm}(Юc ɷyﵳ"bڊZViz Mz9Q=2̀d1̰մ٦YY|dUM^Bޝ#[3[V|EzpcgA}\ᷕdBhqe?Ks ݡא ?֠کӵ9uK?;Bh`rxy,jN\Lo]ܔvw2*u\wg͜)զ jY\մbV+jIO;Dh,$/6rFm Uki:,[8zymgetL"?;v9J+U=$ůpPEr,<& k;\ܿ޽r |tnzj)jv2ȭ_ڼJa};kO[s^&qUfj8E.JM>[_"nw> Ь?Eu뺵ki7Sր~ PKԮ/q{n}&u{^eUxڀ2odzҳqK*4KL= )jڦմi1(&(2v埝ݚ\-׏Fq=Eo6ɞl{_qBJzT5)FFx]4јȧ(yrS۟Rĭ_7k#w=2lf8} Eog1n8wr ]0w(;;c(罁'ySP˄֝s \ WPlq}}䝋@YW u/qΧobFY^,Z/h]sg<=BkmzbMرvW!1u|D|yk(>?.vݷ:>ޕ>v}ZwFEQ"ں^׆PVCU)έgh/6ݲ֔3xcOǃ+z)ƓΧ竺yy9uw߷wabߍEٜ}Sm0lRW|=֗ ;kFc6|GW{S}~[ic$Ux5.gTRl.ZUmkU1)@,KiR#}p5\fƔ5(vҝO2Sln7}ibVSya<,Z(ܽ7Pɳ< ;hagŧȢs~15j=?O|TgUw7.<4c:"r\\TSsI=bT]Vj|[Tfvul6vij!j~gYRlŭ⧫u4j꘢n]ac;|o^z [lZ'15S**{J5{^ͷvM6TccI+uʿ">;˃?]4_s9zTF; Pr ڪP}s <<)(#}%3;'u6[vu5Om}N . 3*{g&|{c>t*؏'<}ׁrη2gUNLev Qf/v}R՛4>9Mw=Dc*gYuUw6̴*_T׏>t0y?}?B5l=PRoC/֊w@4-O:-Y'36ġOfwQ易{Q ~6R~_9Pu q.͟/lIe \yN{2{u|?B. WH{ʳb׾S_nR^Q)F>꛾kSNjNOI JmIЮV|QBgPZVԧ{5uʸ}؂v^jPR[)mߝenN{x2I.٭EOQ*pw) f׺Xմ+ks*&pը-j*4,9va2u<}oM;KKҖR?w/49_d尊4:9ת*ż:xj>:QRgNg;#g#R[a8D4䣷٢$y~v5i{ 6*F2wmNU;qA/ ^,Ko)8rPמ^vL_f&QPZSmjb[${ % SDnun~d kc{m!WJ9rK-ۏ[(YkCEZf&PQCcr"YºroaFg{~]iQ|”s(vADzkڅ(`ptvh acCzaOvG).o^Ya>Vч+4X}߸%.y[_Ө+;OԴ qKƩߔfѵWbEz)a}Ƹm{noߟ"6lK[nkJ\Ts6 o5v=/C>x=te+jWBEAI=֓B)[=#~/Mv?焌(y=wO &>ofM|I1cW q.\yq[0QSUynӕs.%QVG~pRrMuhoUckT2O>wjo#d,o7|>yJpJufcʈm&%߷qUWu`KΗSM.%kfI"\Z,̶z^~e?wcYjN{~~Ӟ\gN:ֵyϣTI;Bגtnj /Ur~ؾ' (:EfՍƕ5k=Sҁp5}?+7k˝s)clV6u&U>- e_l,QNݼ,!S~#j0T? nI<֙Vj1<ϧCjQTZVNA曆CJV+Gsz?5fg}Ivmr'q_YyD zJYa{Ͽ*UjD /+DN![*;],%=c748o2U>5WAmz_E#NK;[Rp،*6wtVUSѪ)T_̉ljWX{ ֯?3ffNZ0C\>kotkbU%mqpw8j}aj1AGl0LąwiDtD ,)qܾ$~COڹE|m#}VUu)>P>oKߩyOQWWʰ C7TUuN'z5|Yyտ6mn tsۥպKJeϭe޲m`ήU?zvGT@v5F׮Wg{ٗ%w JOҮkhս[)I9pzimvoNU?C ~Qyn}fUl)m;JMgP}|R XYKF|w~+9wg7KgpAEdn8~4+3oʜ9S\Qźd{ձZ3N9zZEV.Fhs)UȻMZȦm 8u2y6lsYYvIuɵǼ}hF^;h\D#ju]4tid%ad\}\v giCnKֽߋSi 79՘L5"~]uaQ:(s#q!j;2aW&=^YES(av[;]?v9?ݧzfQw1^E$%5<5}a$M;v?а>Gǜ:YCo賂-6N*#kYeljOrх"Vn{)9j]qLvփnP{CU.=^{4d_aF'?<qq^z=/h+Jj==pxZzR?g_ʣbƚv>}JV*(;o\ϷϵھωZ09=wԎHATk4d^7M4\cټ׊L:qNөJ'64xaٺdU];/ ))衱o]{N莦7B+w$j=~ȻUZowV~}*})jU[:_3,7t)doCv5"+kuǂvi&"ţ)T} gL&C#57}yEǔ2|妭 _xmqAx=dXTde8zU)Cmv_']٧L~sǣi=ɢ%k]9#lKC)lЍͿ;QNөDrC6. }kttE\2ܿjoNFFw$YDYwݙfSo˻,Kf>\Ωi}ۻSb(.!aG2Ƕ(~Ύ^dtjDoBjޡUCRPś>Pݧ"{NH~^o;yA6lǥq=?3n͋[֓\ߺaGQmSm`^IQ;yA^rFI1b(Ϳs2`-(_T{|~d_Շ4ybSWϷ{ݯj<2&"8S#8]JR`a)ˢτ=XFI$E~g|$%W\= m<> [eՊB6$f^T o`F}*-FSp]S=4t=sq:mYگ/ܣOQܮVW6G]IRCפ{}nƤ&M74IS%|cH;(}e/5rv㹰byH[ Wyc km^m(QZEy[5py9a͟/kytAL!)h\j|ƥ/}=C vxbNaSǷ\mz7zT wXq{*E6NRzy:cl<0$?/U˛߃<[q]͛z9pj)ͯ#RR{j%zψBjq`55zӑ꼷2 X2>7ÐN'KqEj^3v.RuۺR<]}S-Nn.J܀)`{qU=EJ վUFWc'w/^?Ɇ(w d%5ig{zYj*_.[_o.n\q.MGAoυ5H>n_{r+uI٩j^v˫6mZu~:B?$:gr/.5 {CZCg'.uSҧyu.Qǁ{]ޯ/ l+Y-=}{ UG=C5/:hӠZ+'&XɭW+(yŨL|]keDp87(Mu[Sg572)jӻ/iɵƕUܾ9O[r^{]6Ux[k|;G{ݏ4jX.G} r[Jzvݨ7޺LEɽ\c[EuF/cqƬ3YϞb̫:9rL0s{b'6is&={ES3%YL=;:6n5(J/Kӗ٩mYxR1MWV|iP_#\>J:fSͪThЃl,~FO\UZFuk O"U3IZ i =?A~syc]NmJQa1Vj M;DVwQlO搏ّôٔ3pr亦w'Ozxnj`t^h~[˫$&ߠ8;LӌzӾsn&עK/%EaNлw;P>= >˔>U.I-}AXuvxUOlk]oӸ~?I y64pU_#+$_<ݟ'S. wֺJ+M_zSzٻuPщUnL;Ε:7wQ"~:>\^G|_S^*U'n02J=xҮSJ*lG~QGu/'Mr_nMمuO4^w}[F?lvA=EQKyCus#ĕv^_o=gM1.u\z^MZ}oJ˟4V=~3ᙉ sBץsu-<9/KKJ_lx-gp9\thGmvƻ"(H+[6isu>n+e;i.e;?|ԯ]'MVQu@khbqv3ISR29ޯ5+vٜkPb注J2Ype?,rU *W=Q{Wkej?.Ue;vvy'\oul'h">jdq'(۳)'DC_\kx_iAY/)sF~eRyJov&n4:yZ' }NEˤU$w;=\C)eV/]c+K-"6 >/n}voZtݸ/kOn¨|jA+OڏR>mʾ|q8c)F-^Z㋾WW.s'î}|NUR h}q;rcp5PT~Nj)'ds¢g;]u?ҶOտj׉Q=T6yj(2݇uRe;ֹ3~`W}&/~줾_Nj~Qt_kOR^5'\|sS̽%!+ԷƣE0j~,yOtWOKauz9kB~Z)_oJw=fҗܞ{ͪQ3義uS6jNVgOT S,5u~Uȭ&_Qm;Y$gy }{ oc E]J,n.fAUSi xŻ;K eKwUݜͅQ_ gü8 F߸Jq{g^Q?7l?Xr9odfzKƛ9fvG_3wPԲ\U7fp`uG{0jq>Ɨ\,qƠsWjB!tjۨ}1lO{`kKm[mKTkNXF?Ȫ۪gԸO(-,"OdTobҸ/> Y ۏN)ɭ[1۽dyr7%8_tUL:;f7xUY<טa:[ޯG̼8̓kH˺]y]zw}~nYq4tnEIS_'5.t3>0q{)b+-U~}]fnx4k2Mu5];ʢ0{UgJ}1iyֱNi<(UNGQek׾S^ (o1k:Xb)(5ⶳ.ndd7 C⿵.0D\OYw_hƒ w'^'D]g6ϙTinKy=,>j})s >ߠGl;Cgc_OtJݿ9EY}%DxRصt w't֛ =oǓ=n٬J)u}ci]v>1%xj9€J˾6ݏYYcG w.I;)keQ@]^և?UP`kׇH}WJ~ XbyxgCqQӤFJ[%vD"dZĜbv.e(~ܜr{|I4rl޹^s7X'P˵O! }!F(|jux݆cPVwޢhh6cQQQW}~֞ϱr,M#o[7>r΋uekGʰm}{1qcnvkz\(,@U|}TsjPoG^Sm7R[ eRE=>y~f%Qx+ߔP.WbΕf HAKz,xگ/̫4ET:p2`^O`ﯹO)[AUKA 7GqݯjSxLq#gHU?}whpx4y}Nd37_Z~Rc3FP㤶ow{OsZλJ^8W@.3-n/bG/ Ryj^UEڶgu#EEj!fQ{f\ 3!y{ƭӛ@QM^/:V}T}Cyq"<<+N$B,^Caaޫd'ZLP[]P^> :ģգPn1yh~fUUOwxeK)Pxqr_|1WG1]7[L2hNUx$>hB/ۨTlm%VUQ0x)#okae:bkOC5϶UfF /}wYpB[MQNPㆻRb'yÞYq{0l4Y5spZWР竚G Cf[_PڭmW V׹ҡ,l7FCrEK?<՜gΌNHÞwn6;͜mALp%sr]>ou #5ufc0z4dutes`+2zpѻ}z_&799'}S/w|6356{aȑTY;ЏU`*VR*5j5| uϋOvU\1d~rIe:/̯|SZЏz+}o^MJ=O#Vo֫k|<9G%~zyhXroY\~*h,\WKj~oFWwˣaɹ_jjZkgx;1%PzI WWK?6~ӆ=(<``[{nG.^=A]s=l{}RȷM?iKԠQo_~$*,=wyNR::~Q)c_߯7+Sn_E_goI _pB`x`x`xxxxxxxx`؉ ;a'2DȰv"Nd؉ ;a'2DȰv"Nd؉ ;a'2DȰv"Nd؉;c'rDȱ9v"N؉;c'rDȱ9v"N؉;c'rDȱ9v"N؉;Qb'JD(%vN؉;Qb'JD(%vN؉;Qb'JD(%vN؉;Qb' D(vN؉;Q`' D(vN؉;Q`' D(vN؉;Q`' D0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,Eb "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`a !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^" {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ ދ{ҥttFtut >?~AtZ_+Ƿ۫oͪO3_4iۨKRFʲQͺj"Cfm5iӨETKFS?o{ashazam/data/IMGT_VDJ.rda0000644000176200001440000000101713762520615014407 0ustar liggesusersBZh91AY&SYxc)@ @gޘU@J6zh Г4x4 M2hm =2&5S&@ A9&L0&&!0#"LODѣMh4d\M݂*IxH&eu&+*ջBEցjR`jhD$0LT_m!I& ,NJO䰎ۏAr~t#6yL Ql+uLVzjM F7v qO9=z~g:bh)8x)*bnx@p"Y6ZFd"at!ÃlSQTdhB84a!jDLR5fΘ=I[ZD)a|W:^cP<$©*p܈Wi;u`}[[JD$ "lSS/a\U1#̿htKҁZ) &+Atzx)„Hshazam/data/VOLUME_MUTATIONS.rda0000644000176200001440000000174213670240241015613 0ustar liggesusersBZh91AY&SY|"Xp@/߰@~=x1z"bMz&2#!mGQO{?)=M?T4 o~Rz@ɣ@RADJ" zmCOIA@4OSTAp7c( ovz"/{ )F"eKBBIi0!M0ɒ %`HU@6@IS)6ŷzͽ S{͟UAP75@E'xͽnqES"?O:lYnիV.u߄ Eb 4ɓ ! HB$)(%0#a!0ɗB5",DL)cP C"L l ֐Hd#`Ѕ%mKZ )#l4̤e0BH0 "E@ XFBZUfsvm`}p@`X`X`,`nli5gL֭3Yά˄-VYL:&DbH/)?"i<_N{Ӯ]ِm%ݤI$0 Lk߿}nDž)JQ$IX@%UJQc¬I$$J Lhlݯz)JRìI$$JĐ0 M5exjU{ m$IX@!BkxlNsk -L]m$V8!&r08&jMX[|dǺ}@qQ!0iaeOp)!Ƈughyo<]|ݞo'i]|&л9xG ǿӕ g%~Ǘ~z Aoï`xQ@R f6Ml7L83v擦wz_>Fb9dD,A3FigDif5s`6p7Cnp9S#rA$:;2pw٥UD/BS`D{8񐄇XPA|P" 1KKPMI$I&aBADHy6๽UU^p%=&jz#4& ѧ )SQSL   = "#ߪUOQ?*?Q@R0&0&DL4=F4 =@zDބ$2 s؂pE@](*IȶI)H\qTUCDLPPS%Uv1pfucZUB[DeO~Q㻏 )NtZt |PH4afsc(yUpy@SWr1q!A !]i h]pD̑CS\ E2B"bFrA#e UpG`Ef2uUHUflFijݴ1\sCJR\+{Zxa{EDca] {:=P H$ H$ Ho3 HUuykPIIR[H]S%%Wj))+5Tqt 6rPU?rtȈ# 9jLdL?F##bDŽ=T}d(qc""Ǩk{먏_KSV?2#N4,!уã"FGDO^ܨAq ;ƌQ rp~ tuitAO_@xxl:1F=:jj#F3=Qc&()nZ(N*ߨqKÛSGҀWxW%?:s>L؄gdI٤$w|v2ڎv$N Z[W,lQ-is_Zmt%n4`9 X9lV_aj ] C{67:ů}8S@m=P!4pIf+`㹅fR+s *'QQ+ŗ(x߳` 8eV<^,kLzn2;f9ݹ|23.԰&M\Ӈƨ!~\wG6;%)m|^?rW9%LryCj~7u4`r[3ed F;[avٗfIM\%~u%v]')`wfֺu}/&)œ,ozrx?e8_򲧺}9nPb[U~֎ ~]K^=ջIYO4߀7jY'>2Y=\J ,zJ~l>@6Iqt@]~5$;J7_m6 Srkvee?]nqE2$K~M$yi3j|/|=l״7VomzBbkFaȾOƽU=h@pC/9d@&U($^i~m5kz}j?9ZnTNv#ir阸D291ovD\rNhdwo¦upt.*8ZĮi?Us뚦جIgeeyWt~œՒ:mGld3d82 luN#_];h Yh}z&d{ؚɦۚU&(.I-6+._弡]۩nѲn=&ѠQ'ےkCېV'&W^#Z,"[Qrg`Z3jSv95xd˘D{^_9lwڥ^v;PR/6ɏ>@TSެ)dѪyUh87^`6Dz-cdifiv4bIepw{Kr?葉" 9Ǝ{D=dˊ\2 q4kx CWg{ˊwedR<7SRe7 }˼pvmsRX€C_όwnߺ&qdD7hdc_:>y{W/7ߦO -E緜a2ʝ{RO߯';꫓ךndpװծsem_u=/UG[w]D7_oO5$>pey@ Hr'nݔd;{J'?m&uuK߱:lj&~Ld:U۵WrlxyȾal`iO}wvޤq e# ^.%zWmNI+vԝPuGVSڅ S7>XMv,\O]ޞǙ~<4l3̗i%,A* '=;lnYwP[!n"I1=zկcraw4~)(LnCit)ӰU U}-x&?M_BmrgLmc\JvrdE3~ j}y7 ;ݳúl3y5LAC xM;]n؅"4o_.@O@GѰ JGuD輨V~یR"v"t>J!zGwM^~Z V]>4ŏ"W'B%GvM&mzKd<4$!gvv9y_MnwښA.k[}IJ/9E6э?TCR&d2hunrКI=Y]ү^kv 0EO8\LN&}"Vǐդ%{k9-n+#1+$ݳ©ȌmνL% YdN@ޱ'\CU5氳s̶⯒ >~֟k78I{g؟|Om블-<|5:9e_($)!a=~ò3r^F7AѬ .8%;A ';LЎ\7Xu/E. ˛>,JsI]F~Gkjه;}e44(%{ퟞ{}V2 {v |qA 8=i9ۖU6qtM67?– YIkWh,_qWFtW'4 i,` mjݿKaG0~uxnmcYx ^4cG^1.=d>_(),SI >zmVy8ї=Z3EVaNr|z6ow ]̎u\yVm2ɧ2,3< xGvOE}9Fwn$qhϗu9M"ge7 uhwK['I~~>߮AIN8<{HܽBq4h99<ݎ\fg~%w"V6ˋ}W맽Il9=^%88 ^RN9U;kK㼛2ot=y=4y:;s4 ZkG _Puêa ,{S#n:@廫cRuM4Ca}z^Չӥϰcz̊Z1whdoAw{A=[0ͣm16aq5My}V91C7uVDx{ɞLNj ||ƽ 蓤z6oj6dUS ;CW-_Ӛ xoHX (SVF/hފ i6:;)bOǥ=ib~nS?u)/>.Lv5}3̂[D{|7ے QÄ23(UK{(hS݄~ܡ+Hȶx EڛAOk+O$n%&o rro69m>Ͽl;FܚV+N)naf)h6a5λys.;wy\&yj y4\;5GkVۺ6,2!mӍwVPcJ)fH4 ]{94YqyoP'ȿ䏱Qޞ>.dxe;g7r⪏VL6Y4^OkWZcM=" q8Zo>908b8׿FߐRD~?OM0>_,5[S9ٍ gہSZ'XfO>J{,fӎm&OQ))JyI(vȦSwt6o\5n*7m<s ;䯗HL|_iۼjR)+߽oI2Q+?Lb.1$< [9"U?>.YuTGcwS^VaZ8|BWHZR.܏jS}ؤk)޸yͮ16:͝hyD=rtmΥҿ8MW,>nt9[`|h=4zz?Ļ\In'Ft8CoæG''ӹrNuYq H;醟w앦V~=ik|bv]/ !)g?UJͮ֘# .ǑG]6Ns1jvȕjzoS*ۑә{VR6ϋoOZgy%ȻZ`Z>ua *ު6ʖ8Ǯ?ikYiߖt94ƜzKR{=IBڮt(\v^uyRQ Ro|I#=_٤a1d3uBͶ7id}w{|w?VQs6wnq3^-ELSkuc~ $P2\{+Guϩ-ۣṫl4~)v)^9aʗ.9{2:wOHK|ۍ{l׾7#L Nvm@nG6%F_,ȕ?0Xܿx.mONo?r߽}e۹ڧMƟmJEhH$=M#0[k--y`~Z%t Rl.kPk@ɡ+yҎFY4wL6~{V݇g:qɃz0r29ď-k, 4*6~'&폃#ݫ65u9@Z!2ꃹCTnEEzEqo'7'=#aYdMN"鶀<_/j6z٪}rۇ? R@nk[>\/5Y~~}s_*]6<j{u?m~>g^6w8 1j͖v;ܩ 0h׺iB5\~eޥDօYַ^䲑nGSحw7]&i׭+ OUε:n*lzRq}}o亷 7| Ys{ޞ(ɱ mI,p "{*SԾ+=h^uSRƳ3ۼL!;qК'u̧܂BrFfvxin~(2r`m2MˣQ}pҎW\#N8B ?^M_: t&a܇ v)3D})$}3%9o`dsoLmXo?Ǫ=A&iﶛF-^6٤k_y' g-~WY?z\.d?tjmY~6: (U:xHkwܟ{~O\x SZvI ]t ՅԣVΫz/6۳I} {<=7eDy{ܶUrdZqAr;夳BumfK{Sӂ "{Iwa-6(`G=hlvh[>39};6k޸LX-VѰfKse1o׽2E>YVL$vsA{1wdWdP2o }4]<>[m9DKsxڻE,m<կ5g˾TדwbrhhT{r0R\U߽}e۹!*'bGM+i[uu/N0y| ;v+N<׋<yZhRz!ՋdsmIVwPPY+(oѪ5++9iOK2zt,,KmNm'.+\YΟߜ▻uېuoϷt':NWc8zԡLR &]LjΛē3H/>#dy6hk$S^=c/kק˒ŧ)ad8~ 펕km"na*|~| _M]: [rTb{5xV_:x.2d+mz0iS(eЁzR@Ζ}^6Շظr{xVXmr7v<u,<)iyN|ޣa UޥXbʢQ,3'Wk.n'˝iT;̧#{$N.S.z?% tu-9,sqr+[Z 9[ُ|X~kۛICJ6>?uT&&]:>%kK6UO3ªk؝qg[/uT ^{.TT9xsyկy<~7hԈK)TUą/)\fݖb\A)]/PEvՏOvOL]ŧ;F_?us_֑y=LRfߐED^d40z>gǨdqaihdt5edxҔ,%Qn۝Jf֪?Ӊ4OzOF["k1=rh;3c='f<^7 wOC3^ESOz=Y?]WQv$㥟,+>'JlHaDu+-E4 MrH:krXЭY۝#'N>ӈF)` M3(`Tӄ?i|юƁr^=OrVYdk$>$bK Ҿ\5 ZWSh9_dqrpՄ/:qҵOufա!ms*IXe|Y{n= }c 7oٛ4F_J4n ݢd|bN ɪOJφOGhdLw)ҦRr^dj>HR<='y2oNtиC3ͤ2'lyjSr}RQ-LY1cUOWUՔnH>4h$y}RUq:&_ict}ӿٝL t֔enzm90_,Όz!ޕ7Woç+/K$'jp\|/!ϸ=\z8o-=ٗ{[1SCŸW "A6wđsƐk7ZKHeCۛ #L"~# .M?!?;-i44.9y[hL7juvۤΤ>pi۩Su=RûR"w耡$SUgH4yl'>ʤmM]UyNWy\pL&;N&'uMΟxvב|]ݔl_W'`9h7zrnRr=r¬1Lװ՝z;szwovJOچL)yz{@46vTIԷу-n?H jDZk*oOm- ۞C˦/v$wJѤQ_G{Wr>Lm"> :DI.<6ٷY{ٴlݿU-i!dGAlZ]Mzh%gMAJzcjǯ9Kzi`B=~ 8s2AOž{2?'8Ƭ*m7wl~g;xxf{S OR F-tLjiZXS@zUk:U}*e?λ|?>n&o>8ۇP`o[j_W};KǎzLjшgzSWhK緻"h'(LmƸŖi@Q޶/ww4vԣU4n>u>!6?HꅁV$%DZ#_)niJ'YG'T|ީa IEHa&ϨCձ-ވEd},]ar3FzIukw 'Jҩ<[R;[XЈzw29ݥ1dye%YA3ҥm{Q'WnA~|}odx{ow9N E>au׹1d+rE{?=^^yYjVwLb=zMI?/ta7.Gޙھdq)qdJ7uvQӟ̜lJRuC9np y>QzrN寺.Zsڼ37]4wV`ydz~}~i+[ЊѦEKL&|Tf h}uۺWxݪN>HSxTwba^lorfO.L eF+|9upSD9 !j[ah~_/ν zvy8uyIhԃM\ $i17Y|TۺXB-\f{lP!]/yF~OVwP^9/ֹuO;q/)Gz|zp"y?3J&jMWw9_t ׃~?¨t^8AON9LFK>=nTV\iJ -gZsOkn 5iTA:ZwZ}弒zn hkF9]w?uآr=;Dwbr@2mkCӯo؜L==31]ҥKǞ"'u"ەGg˱A _~3^o'{cr8{~U#};@'|FOϼҨ&oz$Ljrm Cc b=vI!xK!՛u#=:64ݽ?!{T2P SѠ/{jGGmE?M?% =rӀ_ȿ͜(pY~D-g蜤C-"tۣglBNG}uA; y|n@ɍ_"[a&W{;??T0L1s&a]Zi%St)u$ /j>[rs O)dv`3wҶlZpܪyy[y=),m1)0iʛ}rZn-{ iѿ\SH,^qϠdsPڵ8񘮇֑}&kM%s LnW{2>ߠn@{uɣ=&WU6W$N#3wtvENqadӓmqb5y=rM~a^,7_|{˶s>zě\iAf WNzD|o;|8UGV O:\%ߨLlہG gw\Oed绖qc[ˊ-em6 gu#ٗ?ElKFTҫ!Bl0ϥA{YN'ӖM1ڭzT}ñs{օ-&íǷ]xyj^4 ~8~5?kl.5~4#Nl*JY$R9,yBr*2]sF:*,bN6jٷ-A=y[|q?r;d;WI&Y0L~\gHNꚋo%3nG Iv~iL.tϿl;-RsíTfm#=_mESk;:3~o`Bv%<׈lcŒR~!'%[S"\Zͫ7J՚dw"ۉevX*rlo5xD#x{&{CcssncqQ;+{-}LѳJ6K>+U3j^_4L˧ٕB}ݒ#u]cBf;VWht;-fZf2.~9 yK{ucKLcۻZw8$R4xMGಔְ5yC}gE3ۻx Yx9ߘZ9Lr[o<t(<+2s?ck[bsc u}lgÀd%Ƿmӝat+"Igۍs o^'Igy4עQu;"͛?'^n]ۻ%G&)N/7̓&wu_g;Gߐx֡1`kIb`Eu=2_T%76tZީQ4`ꁵ -OeKQҨw=xr1F6Mjr.Y?Iڳ Y0M[R$مDwo9XGF ~ɇ͸$l y8?\7nOKOO"k1E=) e鬉4aJb;=,:|tD}M3;4\#MaDZ?W,dJv. %mc('þ*6說bo%\FA8v7zwku165uϚ6/4gnD=gm>{&Z-DF/.<&֭zo d1=uç 0OVyӉvP%o'޶=Ȅ\*~O~W<h}9}ESvzWUdڋ5.6F[ [6eLNǷ%Y'{f7e2] lYvbkoѯCe+}b /P嶉h_>9]nrny;+_lCN}\4;~a߽}e۹~7ȻRs<ӛLS}\XeayuwH_hy>J\}w0 3W4^}\jrNԿ<u[=O8| H?'=T뮜껎]^|?y-6%)!*g^\%‹J_w.%|!h~1jm&KmlCV3ΓO06m/kZu~+Yט NkQKw^1'-. ^I3=>YWQXfƐﴴ;wEoډKyR݅xQ^)y> o%yW_5yִyOg<&Y-o7HYidHG׻y?@Dar85 ;QMYu^ENM*/sdd;Qh~R":xt jP y6nhqh۹oner_n-8xӕ~~?%:ze&G?^; ؽm9 {!׵ w-n} 9_xY =mw)@glR=蜗;\G3'5Ʌɚ1x孯bB~LG&I بA}]@ rx.g-Os7He7hvhhZȯӜw2/eD~c_Ѓ,m|e0GeFU#IV?Wy*/8slm|0}T{bZX: ٭?<֜m26Y.ξuБ_u]:A̜p2>}y#M_ 6y1dT]s'mtذv9QS֭E>;}K:DJ [H=kMD/ƨE57WNVBZ"Cy'\n#O2y5w5{Ѥt 4|̇ znזgMg1l\ǪƑ;!oG~Cg{[,bI>u'W7wqȋW>gGVKfA_R]K!pۆ'͹E܃̭Wϳ~ښO3vUL&5~k҈ 0oFViwϳ<ŵ ˅wyĀDo"ld?%fLR;rS4aޑ;vOqխ}~7 ʉ[֒u@/{ko ׺_)E'E&CS ]SBv-C-ک{y!gV5E.l LoVg[yyS$:")SsLmL::nyЛlLzJY޼!CGT?ݸ% 6 ݤlC>'hz_mD r:Ғ[WV=>ԳzOُvNPO^M2%Lm7tȧjF/ bek ߽6ҕן3|wl^mqpw+tkBR3$=z~|yZqN-egBgiyxlP#Ҝ|Jo٣mV# d䰻 hEA᥏d_N[HhrjlAcuf?5 }h+ICCL4a14' GR-<^]?=Rg;-G%i>b|j [Hm4dk{ҾNZ- lpkc[Ypk NjYG/\{(UMoӝuv#/K&U/ ێY#]$zؖlat:лWI i[kb[꒓G:eOnν&f?L׊DyvvƇ7KHlL.qΫ>^>6vտ{˶MjP@M 9=Wx:wb X}8Yr :aQI |_H6s=Z1PO3@oW}̡_QĉXi{+H/B@9EJs=&BEC)7xx>mLrOו CӗY26bI+flǁ{^i }xrL爫9$|P<|Ty= /ܐ {ۑÌ ~i-CVw&NEv˺эmKƈuz6)|abK)뒂鹹o wK]Trd_3à9-ZdzMChfhX zJ޷%o>T mѱS/Q~: 88҈DrHhMS]o bzArfݪT}>bn2jTWᕵZ~֬k3<8دjV}|4y+T(&RSRל-tȾuLT߲$ېC`_hY\;}=ng4*bO|[݇vY3Wgo޳œ-ڸ"wkf2r8lɊ#伱^mp&'97Yg 96WlHo;:wov;䀃9s)󗫓 w.Wuu zǧ/j=CT#6#摗6Jm>ߴ"rϒ rgr\r?oT~$^m8c 8cC dUկ{Ζ%/,)PGق.ƻT@.NTU_65Moy.w(r,g4Zl$K'r{=9tJrM!!o_'W46Ŀe8;nQQz괹pWo{OΎms7&awͧő'ꇓӬׇ^NCG=DNv7Hhd/_CNA$9 EɵYrkVƸ'X9nkNly%ϩsHWq-aP4Mxcepߠ㲕8[% Vc'M1M^MwzrFq99u{.kESԛ2 \(fGzJ1>wy"-ITes("dxHq7QgX-l9fXqi/ |SSߨxkġT1Y%UR抰}jl{(dD\ƸyǮҟwu\֚[k;'z_Bj[>GK$թNjfNϥ,÷ۗޮ1P]Km[5V:C漬q-JzL?9{VY渱y}ՐޓPbgݚ)X*9Y2һwXW߰r=,rI뻔y!-|^s7o{ySiL?lK-M- }gM58;-P7+ÝywTܞt}z?fG&Qq)S:Le+[VK>ջyzpjPTWӎ~+XLv]K-D*NP1]C;T=i=xEl۷JqR~A1IK"_9Sub>[]x{_N}z;YIK.sݞ*uas.q/K9e:z 1vkeOͨ|qetkZMvʲ!w)znWrrloJ5%OYFe~ߦҝ1Z2\ާ֧/ǫ_<Ƹ,qVS6FQ N׼ߛRֳYaռL3wԥCf[YO72SR^ٳaά簸UH*WjK۫vcst_c`*\9jhA;)1kq}w,^cq)N{yW$ :* F9:Ri 暛]w]q|Nh_%':+%xݑﭺUb>_jseT6q讔mPtr̭?Ω^_tG,rg|eT2I=jrա!=9t]}8}á.iN\?\w.W F(w[2Uk 8p*6k6YrGO]r5K  76i5<<\WsK~1Q?_u픳iR]}\J{ )\>cӭոZWv.C7\^˫ *YsAԌq5sO*Jlyaom57{UM?^G)KOYw9;kT|+Wm_5d,]Twr}~Xh߾nyY[Ǯ\0#~܇㕢GS]_v &ESY*o w\s* v<\f&M~wWsҘf. \0ԾJj᷹<ԥŋ5*J[fZS[ƹAb_}~I8Nǟ/;$Z*rnb##(m'fo܋VYgqG]?.qD| ?_<W9gۍh=SŸ &5-*9tbmsߧOb}8Sx)PN۽OsVyj=0s/?wflFTk?`zN87[8yEjrn-8*UoSoaRU1sw*<6mܕxkΦO(s{ /[+psܨ]]n&b\w_PFtͳz?Kr<,F6FeI>jG7'?\=Ƹs9Z4hK?F5]GY;W6Z߶Wǥ?ZbU^Xpp%ʰW5r&͹RnW,9HmwgW>@S> DZuW9Ξu&sB~6dsC5sś/S=]ی{A^%6Ӧ?d͸6,ݥ LQ|\/ϋdPK 9xɇqw>Ek:byX]viZMMfkOS0Kn9b}q6e035qcY+>s{he3 څnH/;>zyh;J4z>qx<Z?_:%g&(A}cj :M%}S1Ƥm[qt?;v5̳jX^|*u|Aߘ%:f}{vOӬ^?&lQ:iIAg>?:6}yo,pTq-^(/1L` UlX>QU}[^>xT%RŶKY+`pqdOx_P|jr3/3{ٴCj獮x}uX<5Nsdz=k.J9遶㽖lzq^4FLС{cS|/8ܡ-.}X㥙)ܾ7tVnQej=GTsM(^ZU9yidڶ{*S-wy{9? /҃3] Ro^ӫ7+֫; uw]_WZ k(KҵnORx.zS-R@f".M1WƑAi)osSUf4{7;i_=aI7 |!^`1kz)j~5|37%Sna@{j)I-gC=T2}zVjƭH?\ *q޹bc#eө1.{v b>{vuX9DŽ{iOMW=7ƸM:_lD%g>zq|[lڠBY_Iw8uЏRTs`$7Jdy5&y[TT>uIuzƸ{O_\@Ԋ= y^*ʜ|'3wQ V<~2 *Yνc!5e=zޞT8`缾Jt-N-9&zz9/2 7QM'3rњV|BRvFMv?yѰh։Tna׏(6AR}C:PRU1]1̂>5wݑf8UPg憔t 5ǥI9%[Svu }s\sI=^jz8{) O1.?A S8?.k+NxmhˑW,(`|l5U>\bp= }]-oVs+s뮑T|ivn).b5kS4pӸ(V+)<4:~_amM5R-x\m.E.MN;owwd(;*=7(Ro9Lx[s\ՊTc#j!:_7yaW&OCߜ5?xI>ImJm5Μ[.rmK5ƕm^T]#^v]5ꏩ7y01tWj{l}[]*Yߤ#s\]cF.f?];ѠfoƘJ7xJ9*Ӛ' +O͡VZo]kPr&&|o(|Me3 \~{o=*'*5N%ݖi!|47 ~0%'پII1./iv3w:o3oE̓5׫+և_Lϵ*7RI_~B1W[nl*꼩*+mTwzʍ޷tKO뇊㠇P^ЦMm8_bj֛{jo^m?ݞh66N5zm_] 0iTUnֆ&Q~uޟSDPQ)T(Mf2(SDQ3JeiDJD!?DK6 +кUV@ۿMk885']p>=8i亞q4ȸ*,s-)_7]D sP=ʊ9FX2ADn_^Ugχ]SN9=)fׂQEg+xVZO~φ 8svKַ15Gŗg oD5{.RsM~4ɣ_8rɸ3!_ jkđ}niCşКmKwO [&S)DVEOǮ4ro֋5QV-tC ۀuXau )nm\m}hp;0z8 89^Zv5r%&w-{a0>|- @jyQ;;< lLӇs3,k7[Ht(/k Z|?M -m׬ȋo4oo$;گ|k;+zBb@kk#Z5{X|{'Ct TǾ^^'F, 5,x\YP"9`>q $9XSo(M\:a򵮫&]mgx#ͰY=Map06O<Zhmlcl$4_H]6ۤ{bThdbK`p|s?c8z[:Zk-/GAЕ;zRq_Sq`tuIhUR8ۊՉ9nYwv8Kts*ֹϱ=da1cL Vbpyf4w*k%5y^_p5O1#LPyㅗR8H/|?5M9><.K^@:BU<Ӈ.ϓ%@uzh4"w}}X\;wlth'&8첊o.I>`qͺvT,nw)\|^кr6fͰ^3Vr-.Rs#-|>S_U\fؗ% 7(:oý+&cH~S G/ຊ.zV.؞Blj_I.v%G)GպSa6Y_~uԥg8C8I) //cf0Ci 5iďfka$Ғ3z=^ +lq}oڒXmеGc^1h_->.t +$hz=F~Lw_HI- Hy$u;r ql1o|4#=Kw#Is4>g0Ss,L)71:WK'_BU0!bοQ}ёγк籋9L44A.4Fp~cx},xZF@hKc]ɉ@ߒ;gG>DK~Sjb+Ok-p>6,m> hyY׺ۆڦ)y! &to6Տ|_:NFW$AwջFGiWnFwF-] "<<8:7qzUڜw wG~+k%h<"܌}fkP% d <-Zzyt Yj½(XOř/<#S XyȷgqH:GRf~ *D8=h"fkhɌ^]-΍w@>!Qj{ Loh['}opEÕ&yY;h_N8pT]Q~i qylX/Pď';apJdQciX}YXN`}wq~pgE*_#kn`Gk/O*Q_~g\R ;MZ2!rm;0 OBM_/Vs vgZL~">./4ગ>nrs5 _f}S6uUi<\mG5+oq%ˋ ѪEqdw40/iH!7@e>"WBKOPI݁}^%s5ċپ yP K`:7rʣ#,jU+0W前T|ɿWapTS\@^ #-5@qf EGYZ<YveG3mI=qVDL}Ha=wtu2`mw08|1\nrmDZqn5cV'S%@Yv{޿LqڜJ4kJY+}Vڧ5m{.< ({;t~{|ɐwd㷃TKZaQ U`ugQ&I].sJ>^܆쒨"a=r9R[{j$<.ʯƥmiXެ%!fZ$t9d`pu5qEOXa8k>gй/Te ƹnXkؙKoj1wNFVc  -xtѣ |}^y| B{ؑ9 ^V~ַ̽c* sV: 'l7& υuv3;dَե viB 1 @0o K olk|_m&,yD U9њr#zӻ-Su٘Q ctuO~'ݲ8;bjd,PͅC/qR/ } NH@ :M+$vCf/|hn,6]w k>_ |z]GgF$(E!eI@qLgdw C-A>*%KR: D8:'Hj{/ j6L3TMg/U`+V_{oVbWr]wrϊ~x595E|QnvB ]k5*e͞w69k`eh}CAAUޫ*Ni|f[{d P(}k* euCl2Y<Ǚ"介*-Tѝ8[w?6=sts/*=HA'ŋ&y#j,~N}[;: :ޣ"6=pK'eDzvV:x{<=/lH} ;' wwwP%]<<ן+}ʝ+Qq39@d6NLzsTM@'6p U_jL -oA}vWWȂWsIqԗPGm~n:nf7?2Bcp%ۛгFwrўWA\\.n?Z ԹґW+7At <$k&{1 .vwד0l/ǃzؼҪ1hp璝XwDBm}\vוB۱4c@3oN8t=y`6zdKecpހ~D1 ԟ \,KnL^<˷:螠 K84յM7')Zܛ=js$Ο_k o:|~uxC2jՐ3Cދ=ӢB~Fc@-(u5Y쵦r˷k-E1ޑ,֦>SrM~ǐ ޗCsDhV޾&>{gW~TF ?фͯhs@n'9 }_/8g0\[Je8k sct ./kMK,(Gq⦃vZ L}!x̞ç?v11v@PMi50aɍ@$bZW> :& ,C ޜߚLu_t`}G(G=<fM C+&"Eܥ[j%uLC ''I ^bfH/ v_]{ۻt^`6%s[h!u/@c?VD)3AGو .݈\$?b|LM_I"eR"ko OеNnFB$WťPvCˏŗs:K(Έn7WP7O+z}>"| Wc>sy""a;{X辪o ^M7j9Z]qRA3¨["<1(܃űu/ґ#w=yWSlPp6OijL X15C~$!_͊NvxBS\o0֬'ZE9qZ8Cq EzNQ<+(g|>>;-@"[#>h<;mγtk_ _J_ːEm?MFV]Q9}pyN4XէLB 'mB|Űphh\ 9iss}tSURHh6+o$;t? 5ZPbE1 4M0:PGLN@ՁH\ 8Z[2XGf[]3ܵ{F;h>0xOt=P⯔\6f B㫆kQ~_5M߬7IQNm$p?X0y|5Sc/O T QmsQ~8ݵ ]<,B"޽~'Ys͞_o.0to݀EM"{<K?l;| rd nJ0tF˺_vɊQ@xOuGDj*ZQ=ZW'/bG?5h_4tZWn zl hK>VA~usi}nzoק=rǞ=J`pKw@_'sˮ a &2YG9tG<4FBfа _*%mZkt3/D/ףּ5NWJȡE^hJ,$[fHï o없7ۅV/@?hY|/ 5U [v18JBy*`(~h|Fv+Z;)i]|֪#?m0^h}@rl4D ٙ&y&pkMb+ PF^Z6|u>xc*W{Wynjy˱Unrh>emb.z71G8eܱ@>U46[#eFOӊ1tVz.=+O+NbiU v\ YeuX.rd7?9bsmj/ܿ|:wvuX<|~@o5?Y2YاBѭ"`[|;v߂Cr :Uo_< [Ǔ&|Ly8?u™ _%nW,nV'wIK[%CGƛȆ@w -j @J5+{WV ;".Wv/[^z8mDdz,Q%}1PI E܆jX],*0O`VD <]zj(yUg1$9 zovBGO>RTfp5Iл6+: ȷP 7o%ˡ@@qXX7.-@`{KWBgA P.9b𚜹)}9_"7w`ho }{-2?E>ZWZQ>%&7xQA|"ZP(=_z6f:(u^&MwT !n di96'-/BϻnIN#zrmAwГ֢u[udىuz-ycfP@#Wu!Wh۝&6hU?r:VZ xn&x6BvXU PaMvavi. \mNC!Gְ˞мA kP?[-3=SD^C9z=|<{޸^:C-TA/V]yZm5P?f=x]3eMhDZюmketq; &.'B6;zBRQ=F^6>Sjbh+(ُvSQ]tˎ^x7<Իcr3f;$&W֖1H7^Qw0t\/E=m*6R1`ۤF5GB˞VN(/6o*`^n=nZsV^yJ"Ԙ9Ol>dW,f/Pn3_>[ r}kdF_eA7cnn.Uϝ,VK>.[U9a+"݀Fv1kF:\Y:@oOsOP{[)@:)4\vEze%иiJAUDIñuHٕ& Z%^ƢO) dvn*G 4_Q ď^LGz."?VVi<h(Nº7(q2KJwB>{r6+@Ls@Q8ǔ%Κ#X[5ڡG|4q{ahZ\,keٽ`oi($~oU&ĥz0V_'M $( TZx\o)Y姆c9ǹ~0 lfꃐjz_+pium{S& )WS;߬W>BCӲu˰ߛyfmDi:uR+hz4G<,2^- [$ <IlkTjyvY④Cǽ!k7`TN zUN+Ȁסξ_!pPme`#9Z1:l(Wcl= jy,O: O%s3>.k< ?}xsWɄ*IAߏq7 \'$ߍm!C߸wS/6FBx3#g6θM`}{ t|+YZ^|Xe'cg쩰}!.e c08rP!~iؼ4BA/kÞQ)Jޙ(~e]gW@s n u.Bu~ŹP|, F1 죧N 7CkW;v} |P{( J*} K,(V?wR? ^L.;ֳU>5b/YWJ((ն⥺$C)]mBۡnQ^0?I*|[+OiSCzs3 dOB{sP| Ac1!BTٹ6turCy3Ah ;?<>j| ]x@i-UNbr!>[عz~}f9i|c(ehtLx?6sI^m}LȤLK"tZ̳z8@'ai\iyi e?AcyӪ$p;|ke8yn/Yě9oA竽EK -{!:eI+u<"7y9 o+%^xB bw}Ls$!9((ƬƎQ>}YUΚD#ќH'< 4fԀ@9;7,o耬ft04tgM}dh4M{D**~2Lj_pU'f]sij][*Z2xo[(oQuvawt'tj ~%>G>-T'rLYhLnjݫ_`tEUiM EM=Wl|x MZMHX}gjsr>5O'Iw^tm],,nȆ-0^| 핒Yit.Г4܍Q86ڃ{zU Ry?QAAd550}9h^_BAե-l*Ir_'g?Wuk[)fʭm]=qKKc˷g~& K`6ZKX2L堹DnS2˝ }wWbHgB5b߂D74uG`Ip1? ?J { h?:@yPlk+J`lG,9}X_U~Q"w*Ϟ=S(F*6y`[?> {*]h;7wHlq1<o_ ԥmɆXE\$n6;i[evX\ZOy{zxA 9kH 7Q7 .ۊO].9%Cvy{ <;n@5Ke[`qOyGp6ۨCR:6 Wg2Xʋ)vĵ1*6,z=ԽuW.k6׽k*&ⶏOTZ.UW2sio@W,@O3c/05^jah94նfɞ,O(<[wFjAj lIXD#{͕拓ɲfd<>Uj 4ֹRؼ&#T1/biKal&x_Km:8Y5jeT^Xe[/1S_m"&O-FEoᶘsmBMM÷ZjjW(WO婼 m#@o`f0Ozʞ, ĪsWvCE]SQe?hS_+}.[ μJ.^Eвikŝ/g=Dya@Ntr)N@qx J/{[+U?Ύ^k5~FO \k}x傃yʈcԛ -'M/\@W Cu0gJDO׮̥lO 'J(ϕt斱*̷5 /h\>Z\| 2{aqăA͡б̍}X5~%uyC ?>%3ڂ o(|6UU[ԙu4'lzƑ1?4͓Z"4/G>FBq3U`l>zNC7u7Ӟ)#ާݮr?ݮYfw-Q+ =5_jBs|?_Bӻ:sN[ͮ #ٱ9{ mF-ٻ>ICcO~қQ>e^2 w<ӼTf)%brݬ}:>#Gkfٵo/uoٰ$ꏙ/4/u5uT~XqYq";']ȥ6gGThOwfmsomм@ܦz]{ϟc5G) ˟+us42Xe"^7l\J8)m=-|> Ջ" g'w V׶~{e.|H/wY[L*{} Ï_KXgzjy,Gc nyOCJqgqP}BMq@0Y~y;c.PLQt7O ] ko(ɰn{Dp+{aRz[&$Lޱsee<}%%X/'/~7{z3q95fMOD4+M%v'Vֱ4K$KӁ@Bz&S|;4HL¨X^| G?_Z[fcש!ݔjd`qJ5w)3ؽx"p2Χ/*ybe-Ttl)žGݥy/ h36+l#Pyז|br}luI3k3T<k5e ɔ@x"u'ut3u#}ι9DVbxq0I彫"U@;P_3l~#1)x"7X?=Rsq%X6^aW•_;}}Z ֤wvh2N zĨS>eټ@{6hE!í>Q(^ cÄ{V+>tDhNkw+, ۍՁ8ctokRs}Dbm>$}lC5Zsx&Gϴ/=v):'b9\ǔ. ,ӵW-;x,ɔКoEyK"bQ< ](4D3'B:ou;߭1 k;Skʘ#%Oǡ5S:PkӊԏoZZѡ}_⿦Rmh. =Vſ+oA<GclYh.)/@|G5:7W|VRP`-84hG3 K .m|[>?#Ӂo֨3¾O >rzU#4_t|Y-ޞxxjň福^l"݂ J">\4>7^+(*{v;֍~nڋf,B~x#!Q}PɖtOu]"h_bWOBqoa'V0EA"uE@xn[{SItT}¨upZ{g_0nwy4-յ cO*䴠PKMI+9Ye>x+Asմ=&'8ϓ!~hsh- ]ON=Ո_ox oJJs+fbD-٫E,)|P!hữhJ-C|JliU߉r{y@9}J ʜ<.!J榉3yAd͋ƟR6QwOP|gm?O]vYe=r⇧Z\31|̳)9@_傞ZM[C>\*Exg򱥯P}qVa|nPt̾iCBi){j٤聓B], on `MHL%`f`t6xVVw~3C#Gz_;m]]F^[/>r!ӭ[>,@ZO'W<f NczaS7TU#rmeWVgHϱg.4v<l.&_K`.'AlgtgOe`70Weg}Q ZNDfbh w<߃A w?aKY+\!59>j'}xs6PSz⢋!ǠOS_0TEubuxֽn;%F Ʈ*hMAĦlhCmsjvV r;LGD;D} Myuu&9еd}WP_=/^sG7[_g(m`<?8pZWK޾z.uԍPxMuZJCF:yW3^(`g `(^:bZ&ZtϢ.BSk>}[:#ՀD1kڀӾ\?lMүc]T& @(QNJ(ۢ|X}ܓ[Ido:@ 陼6;׶@vޕ(Ϛ#D\pM&n /@[[/^b8<Bq_CNbg<9c.btǨ[ޢ[{Cu>,6M}ጦUhے?w,{dfԋDu]&# h~fx_*yO]iv&6e!hIףme^hݮq>{,AׇS;hhۢf43`4|R=owҪi W>vw򼧎!M_a Gx[`}8q'익-s~S-p|f1ʌb"C0?Yͭ0\`&38k"TޒI>3E.Y8s$Z0*J7~h2Ÿfw ǫQS}2ֿ5G #ݡd"}\:{S{o T/3^^֋󫅯rm8MtZ]wNnPh ;㖧-~ H'#>-f\w-ZG׌{1lc1 >",J+8 ]TCqK_jop/Dԗ>\(8Y6S:叜ZYHgp<{g&^x͆67o=]UXrBqҹ"hX$Qs iw,|s=4?A@w(>I"}oM]s6l|oۓk}KxW X=4ٸpŷ>;]6{W<S||<pYqa ppȔwy)RGϬ OF̭A;w^]~W*m(tQDŽ3ůUC+11vWxXv'T zGɎ~DDŏ/b~` Vﰆ9L.C݋U}ۻ"KApԺٕcm?4Rշ6VK N5o7S;n w^Օ`KwCX>S=V7B 7}mp f]?@߹<{ӕ9#b6*qy LG ӼBD|`!N6q@o%Yoݧ:W?Zx\G1}@JOo;sU&b6%q3Uz޼UDKǑuí:mp;r%䡃iR(4q3U(r4lŠW@V!'fw7N˝̕נ;].9_'fw/}*܌%q3_fwvn921*}wB[CS\Յ<^aTpYJ)o_7S=`ٶ=*|ˇ|Fꅸ;mp3KXJ͞~@1ZFD^Ksmp3i*?7S;Up*d=k̫' XnVkp?]:CI>(ٲ*: {쪲(Tiyh)WȬYyv{w eٷy oÈ!،#=oʃv7u`*:|]"{JegEV*`:{x=\Fz#_A$CYq X1h!):=i`K[ibqH>'ij$p:}e5kݑʡBhnd ZRpwŦdlܸg1|o8ݰN>'А֧ު&I|>7wshX7B@Y|vtzr nm?s)D0:] voGn! Kj.QhZ({2dDρT1* .7Y9 C*} `(}EW"Bַ"n`΋SWǎg2SQyJҹb{ 㮲G>y0y}zZW]O ;ysǏS`X@>ΡNGdh<׃$!]/ _p<iU< H$YuΝ =XYQYZHcp?bPg"(/::/ic9MP;(is+QG׽Z%SlDWYnTD:Aͳ~ _Z(^ۻ;'jZdKasO%u)e"Qz2sHωT,'Zq'm| pzO0%ѺA@w`\*#0I;gq:["p'iAy*_tI/qL﹂ٚ^ {E GV:o=Yl4?}_O: G.1˖Ag~SYut-GVAˣE#(o}@vL lv=;g^6w1}#-VTR[Iw~ Q~^OӟώE?_'#ujKE8.*O=z10GSCtס&QHw)`qSB@j#yuz,G-y}D+/HތJIDS+W~;[dsAl(O:lO=H;}&<}xWEJ()]c;a DQ  Q 3,C )'h DK|¡*y};>lC*8|f5s8u 9oyB'g޵pkkQ1v#_:M(NӋ~q =KD~y/ۡ;r#ZĶ/Y{Z33Q]F|?X']-/o#B y ,_^qg~Q0 r[[и 9L<Epq,P`nGTߺ3P|-vN(^/QpuD|R.ůꊹsExI/LJ@8<Ƨ]'KҚTSg)n4Q%ĥ37MRU~)̡K!4Ra__xqy 3A94w TT3KfX uF M'XFƸ,N|4;\${UV0vԓq0ŠT9:}g@|ݿ.ύqmvY-uP3MkܺW?Zm~7l+!f{d|W`B$:G.6fq F^)<b~y׸{O}gZ0O ^oRY6i뢪/#-I7Cr,Nt`gfzD H@@tkGƂu YܛgGטW߃Bo XL> 3 P+j\+S&' O T|SZs+|0X~EvCu`+v݆==*nlQ ;-~^](nPBϭXZxSWwY<^Euau}6؟ԟp@S®a-vSzTp;].sJ {ߺfţΦ"ƶs`tl5ᢴ2ˏ 1"˿Vө+Net~&i,Z-tq NA@ WoP2 )6O`_Md7.6P_g9 PgJݟLˁ!v1~1o*#| Jrr/0X?V ͌ށv^Zv>iǎ @ϱ9pX L[MMzyrG@Iހ#m ha!u1 ܉(bӅT=W%7;/ay 6= ̘`1E{d;}b̡DA6lΉ[azx*TR40Y5nBUEeXY{P<[_zF$jڴe48jOM#R=̓lOB?mwn ޮdw=tZyW;7M`կ|E$:`|6ZsN|bHa+˳f=J%N5ICé 4,\V%LkGz~$63WoR8L3V!E<@zzRU ƃ %}q=(|s)f`Mۈ|i[LV$D{]p] tiO@3zkͭz~3iHό2 _z 4yrf%v.X*Sﴦ,+~Uuwz=ʏ͝vk $}М "k;NCZHw.T71LmHi=cZy[)Ko8eU Ւii^ž&ǀzWEG_Oh黓_3p]3&]eҪ,ZWE3׬%]-41Vd m˞~*h{;+#|ÇМ_vF?:9hOo(xR3ǓAcn2rwm@㮻p"1QSo-ϡ.tޫ#ld3˖ۄ| ʄ<櫜vO DzFUHc!oxy2 fj$s\.xGa\@|->;"CkB\*R+YnU=ހ%u{*\>ڡP; iM(W+=x}K(ڗ5Z-{"7ٖ;-tׂ%|UX|q=NĀvwAz_Z.L[jIJr×'o :5s/D@s;jUӨiSG,SyY@i>\ segwjqvqyίԝs>@J]utePĬ1` l EWƱ:mTؓGI%K8E^VU^gkzs* l}W Vwo/OACZ\amj&DO ~zO좈Mh˹*<pI=o,Vm'@-]ͽKb*\fG~""]Y}ka`W aX2W>8lp&Ѐ4l]/VՅ;޼f%s̻~mrw/Ů:' jR*[c]Bgvy| [Eia@>]%DQkr\!e]ֽf'U>K>z"ac@˫Xhѥ-''\Ӏ#_ř)5rVKg(w)ZS gf^ $K%>g|"27crִGD)h %IPo%~G~n*4/J) $CwZL` ?܆zrLQd B @r^ #>Z{0vfzi#\{ "v )aq9O~o OX&E)@4N m$2k@9Oh6.|{믞 CUK<0Or_H lاav79Yޙą5{ą:bqx} Ǿ cASKn\R| /4[x7bO'n=3ܗvJh0`7ZxVlI]6^&a$Z ,E37ujsJ"_K˷"f` >7-_5ODD=v@ߎQMn:*1ww.npCh}H4.8n?ԧ銑-dCx^[2=(C5QKI ̽e4EnWS߰aQ<:s_Ldc tj"COC' tmh ]YhcuvI>K:?} j)1(.A֜2/-EHa{NAhٴۇ|rK;4T7M(J"js#^ӡ 12o;XՖ}:^f؎ۣXMg ?[]a0[u14pcÊ|ͨ=Ƥ c'%|߲V@V8܏(F)U!Z݅ @zp ?KrsgE 7Rc9Zw /yOO;@ ҙU&_Q͒+üo!Ć*5m6&(ybGl\d:#TR7!՟|Bwr1 :^4}<.n%./12ܮjR;Df@.)9/0~"]fV 8b%n/Q#We@yK%2cAM A+g?綌EPW=]e F#X_$R*D{>G+L< 4ѼXh59+EL0\^|PݴxAW@籈BM]KcA\<n"ǐoL$Q}u"kxM)" WN*E ^C@}pp:o{VCn=xΛsrt:teCЪ,h6dyr#s$)w]&U?/XZYOm<3EiU= h;Pe{7@VxNe֊?;,5@|s^M Z>3:9O$|XDG_X>vE +8 Uoebp >]f=8{~ Tj{)[Cfn_\GrHk}mn~Si)rYgbfP)=¼#hϯ ?H=W_mv{ XK#W HzivbMBIO&vwt3?!&rz5V_>ˁV UOx` h|%*o݊@`&e>HwyV x(eO8e)ö ȭV RXZ;eۏ{OQVr1a`Z~~Q{잼>ܩSP6a,fOw©mխPL3޺6oy;~ |şHxM9CUoJ6A;_}QXڥ4Ćk3O;&22`_q -~$ݝ..-{?Gł֝ Ex'Э|sݺ K+JM~_֦߀z~P+jE9ʇk6迨 RQl I  gOcf#>p \؍1AulT ZQ3u1Obx K?9 P|^5Zy«uT|Q A{+_韔Ey3Kxcmφx1Je^A0.N ]'sM;՗]᭩2Y[ ZN śZ+Յ+}O ;xzuO4*;9 rSI>́#S}m l ]r7 @^XګdMw4m+: H=>Ro$!El.Bo}f{xhyWZhM59!7@p?Vgbq½'c+ ~oWJK-7]. SFM!pL~z @q4^ -[(ȲzѤ?c; ^eCoF1&'3w2Q,kTD?bZb"m.D^Bۭu Vb.t,2H zظÆT &=ػW[`G{:ؒ=CSi9V,n x`gQ>fuF3%d~4GfeM;,ko?;y_C4_3lmmhB#}3';59Y"K,:(\b,0q妉 & 6~HfHO k4e :18E-]߀7@]vc.Jv}@h:TP7|ۗMd|ʪC&q&17 | 1[i4,Lt%.NAu"+Bmi}znbzxu@Jl؄_ yx~ ve#Ep;6^o~ uy\Ҁ`iG }HU֑T?\O+$Syȡsu6,v!A@wA^oFcJ;bq@?oFNW&-ͩU+v;4Y}g6V;_U<̃;ap$M;'GK Yfb BG^Ѷ9g 9&tzjR7\nXdč[|ֵuqc2iwRWS6^#%^݊xіW52XWҫ"_d9(gbоz[7kSph7xQS{ny6ҵivECkm U''" C Z=T,> ?H !L_ ߖk_BTVCYС%о EqGxh$FD#B˖xs(m]iיhGEf;Q&p=6[ީ%T40CqN]x |]C1*kK, X5'5C re\E2E~f~퟇Xy? m'Z^Wx%Ц:V+ux)q_! ߌdvOݾd'>Io1y5/OC׋D4Θ5xt@+=j=jώ!c~OB?I 4bQ(e1`b2l)ɥJ? t!{9m.[մs径~;<:H+P~9a!J,?֖muNOWx+:~lID 5 $")%v5Z!ާũ[@7*_@_ ht @M~ƹv5K7cGʅ@`P;T«ʻ3j@\k$w֑17UjB KXyCn4~99d ixY= Ͼ`hDp$VOZ: 4@™g2hr[z7ˇ΢ho S:@: SK U5hS/DEi-KYz|(m %=B<ub!4s5Muz^~֛@|]G~]=5hQZ}#OU#޶̋nCkss]O9Ł\RNҵZ!s\@|&|: ?1e&:hTt⣢[}rwEE> 冟ׯm No?7;Pb)ũ†Z|څugp_ٖ̫bsyl~cWG;KsZH=cI"^NB r::wʊz_/xY@za8չyS0Jq+RirT4>;?3/ E_$>:TffOWM2öAn:쁱".Y:W#0Xs8 hQD]"c ̇ʟnUy`j,nޞ}J:hr4FȆ~,[եΞm <гw5*t?k-i*co}3J5jI)fs#80UҶ S4ؚzZ*U6C{~>W6^<T~N _|>c2~?T)X oN*TtY;Kܾ` +: z벻0f3׷D&VKx?GJ,Kg@[mU?Я0e9vz)c ,AcQ%wo@Ӻiy9Wǡr&W9֚y+giS rR- શXjD)G٣`|cB{~`~ /X=ȯ-fjx̣igVǖ_aCsˀqz F 1̋X\PGU種|#2M2;@Wrb\ YThll >;05q8 ͇T z东Ua-\O[X߳We`9A=l0hIZ班2f`"dlUkHfM:_CBzn. Ήo^ řXCJjđ \GpXZu-8`:^n` =4|WGNt[d8#'o:y1T>viPrfŷ!}(Jl~S<ȕUi=a,ϼݴ}NH׆${wt_AW -9ƛ qmi)Mć;Na'G45 ɛ6G-%~7R&7hڰkyc@ћD湑 (r.y #}a-c}ہtIukyVܰ=tx" AnᚇtudƾO¢=8= QŢh:^DwqB?m ,KcT[I@@yZ7;_P> &SG zl9ˡ}\x7JFpZU07m8BicQ\#"!86Arv@?Oz ˌ07 ߤnW}B8=t0Չ{Z>0n!cꋽk&\ -'P2kq_A꒨V{f(ű&!hԪ7^&+f˻[&a ۲66n@saN-k{ 8aANA@.vBz_:d儋m 0ݾ '(Ob^Jr }˃oQr|HZzr@j׍+-+V،[Ch2.5CVϡr;\ #i@Z/t%Kҫ@[7jvS=x>糷7p℈T< 4{ԎV8` 㡟Uxh!ķq$g/ôN&^}W q)}C ><2Dqds7d?hm`(_^f7!]Aڢi'fBhݱDŇU)~Ѻ鿪7Af2ct?|lIZLd]Qm;%y-a7"ҟtmcWBǖ|ǎJ3B.NK~s?G\~/k=w!4)aHIq[7v"cǗe=}dӭץO4(K[?0G?n-68,NBz<~h-j_NJC瞞nH~ mTN|9>2;'.l5(=Ҥ]Vtٛ5vh~aUޯSk_r9?$tǾVٍ#p;=ez\[ѱ{sv=\(Yet{ʹPFtj̲V>L iG/Uۏ4h)7WO<⃅ykfѽCleOJ ԝS@z6~IO'i{志3 ,ݬu\FOb+1[ҡq]TCw eW8^xEw;`xpw$ށ;x@w ށ;`8  2Dp"Ad8  2Dp"Ad8  2Dp"Ad8 rDq9"A8 rDq9"A8 rDq9"A8Q JD(q%A8Q JD(q%A8Q JD(q%A8Q D(pA8Q D(pA8Q D(pA8Q D0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a, a0 0X `a,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X8 `,cp 1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,EbH "1X$`,Eb "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`,E` "0X`a !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg !3{x=Cg#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G#s{x9=G^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^"K{x/%D^" {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ {x/@^ ދzzzӫwiټ[-Mxfr(ͣ-Ru|CqsOosw]]᥺RK)}4u,lpN;3"M]L'ze9[wѱ. 82Q¬Q_\Guԅ6>mJ^?x'(hʠ 5u{NߺЪԠ*ӽœ5u+S'DvC|4uލdž-/ө<:7 8^S^+)5\MYo%NYDhL~"l }E{Zsdw.%w= 5ž2jzgK_vSa.ZϠ ݽ}-5rJp><_}љL )IJړǖ9PJwmIQi)j>Wuqqz}'Pa]:۵3 k?C+eRRkL!eeo/::>gܙDw=uShܷ~v%88mu6ow_W&}CzXwhK/a6I݅kgEi_9ZRS=0dxe\k>˜2xT^T]mNxi81Sl|jk:^bL}t[yz{.iSn씔F>k)W;ίIG0ϗ)yRwCѕL+8V,W?/&)'d퉿ƴεw<~O}u({wkݸPaw̮- qcEtR:>|o>OZiYnȟrS&ݙ^2W%ϱo y̼COWKv9,@-cm0;V8ĞL;uOB n#ˎ G|\^7J)O=GŒFտ+7vhĻ\^^mK/U|O\_ź٧w],7**ݻKr;$07a J"F?ەjV3)3oQv_KwrqkL/RJNHKW'>>׬ھ#gZLq:UE*q'&u蒖j^ۖ-VM~=~B C i9އ3ݸ;W}91_|!y}hMz5^5!uKGP>[.Qkկr Ŏ;վh}~#(>,)vUV괫B&KGL(j`ukniʬ}<$2C򫧩#c(hh%w&ZUe:#Yu{uoں~ЋKNkWxNluAFں#Zʡj7|AΖIOmOx)";6Y0)cjaڂpJ]tJ1\q6ݢsF<<ҾZ N`eB:眴Aiv`dν;Mm!wO C.6&da s^y\M?k0_dBfNZy->S]($ݵ8r~WXeQuPW)Zųc:iYRxU_@\Tғ^6^".x=0 Ѵ5}C5u_ݾK&o4u6|{志s.ɫbO"ߟL~w''Buq_'\RLɳcH᪩s8Jr)Ӻ"[?e.eo7U6N&.#ԅT?z fg̳'UnBŶ5u6 ;m|y; d﹉oYˆŧMv),9#o%M]Aۮ^lOLCͧHEGGn(izպհ'aTf{q(f᡽˚V%3>_m^#)jy !DzFhDzx.DAS~䷡k+q)Tߠy5Zw^G@JkԳJoNtMgn^kU7ҬVwۃӾv@_Lz*r;}hEw֤ZZdgwcކË)cL5ƆfUT)xwϳ}V/o粪Nitף_ng4Qor9;mt9A!Ix"Zڟ%V[T7JNT}A }m>"\3M~̭^g.U|Q'(r}q_!=Ӳ݀C41rN9&ѲBwpz\jhE nb"j\mZ/Tu҇'gSպUjw=ǁuSzv~hv!A_-Vth`7-l{v>m/ݫ, N5NJIeySU[|uE2n@MIg9]Czhz~;y{gW=׏#k1evUg|ymSa;51zَ0hfr#k]}aIK,#uY(fU$͢{׾_}*ƤǷS׸eg;2nTxPn>QO=‚{Si]c.m{=~ izN:|EB*r_U*Iě}tb鈋d׌R1J Ҵ5>Z=h7ծC6^R7T(׬]3tHϼya7iY 鰦λ({noPak*uOP-p$?\v\9>>ݷ4휦ζw+_|MrNj5u*]K9m.jyv.M_(Ҽ,Ye٘{MZNwZwX?LIfwa3kbc nsؔ#xO'F96 J4N.fs 좩Ҥ籆(~|w <8*MJ%LΥ+̶O%[}ף(o>4$g%#6j_zcEqgXYO.%~{志s.ɥׂ6!ظqd[`mߩS uǷFORvVZYS`HD!_ΟpW21rZ5.{վ^ ރ7j瑹x$~0Mqf4u^ >1":Wrdڻz4uBnngЀ^PPJ}oY}nQyF8:̜+~GnZ;͚-pC#׊Yl{!_ϳ'$L$Cv' \γw6(K&dBC]?2[%^>4E댟d9~ӂSꁑ&[dCˎ>PR-uJuyI3(d6OW*YyGz-%EPeJFyI9^W65tH>;bب׊'q];>XWNnѫͅdw{TrH۹}h$P[uueŝrY>^X3.R;EߟIˌq%s{W GSÑcLc8< innD=Qƨ:frrx7=N|ٶ[/zkcZH:6F17(Rp uׄIz>%߱˒ﵯf>ĕ!SSDi~|S?lu >mq:WN޹O[=a(2b㽝eU(nğyQN䵩p…u_S1d>;X㤦WϢݙ JgkOzM*E$TӅro:}ys*_Xj; g^lsP{z39Iv7nbV_|'&h ET0c{ݿ^{?璢Qq.RN_- (0:󸨒 -Q^уW:S0$0XKJu5vr?zڥ6o671qv.%uP͚ȴu74,' /'7&_nz! },rg|\Ī=ھEL꣚W6 ªOԅYj-e,0n}ګE1kVQΐ#;yL~v>O-/ϦTodS贁+1"?؊ie~{8o59U;\p^ %pvh>)l EÅU!dںYb)ӿtṋ)*uu?Ĭf/gJkyqpXA2-|h9ur{Mzgu{7Oݺr{=aͬq>E5T'yT'yz[6V:1>ZK&oM z]qCU,cnr!ELl?s|l]am߽U[Z8&hJi>N:讧k }J䣑Z~el4 ~ezʷafZ^ۤj\yRCO;žxa?F0Mn*wB_hTQ/(ڐ=SVXRR U'e;{uÀuRԻ+]PWpD~ L5̹^u-:^2>_s?jwXM@n}7(TǗ{[tSiqUI]T]de~+LܸCg;EqeƩ!"gyZm¢6=?elSKuM)$naj[ҙʪ~l糁Eg`fY j:l({n˺D}{g'}}mj5Ile=N,Y-w?PǻMۿ<wVYey\$ݛO:hvyX۫y.uTZWE[7t>gm?~^;Qm״քNBު痱phLAoS:~\77?K0>OEʜ!فET4gh|#?_q2N(_}~7ꥎӴr"zBEҺ-ozWz!M/M]9xxgDuh;}_x?;zΣ/xk{hُífSʤ3kQo'NUlo_VzkµTiz;۔2)ܛ}Н{EǙr$>>3׸:>fտ& '4: gNr)ߣ"F{[옦g/Khm _jꜿw:|lsys䐭?W@u#"efi 9TVhVwrS19{د*Az-M?ԆgGؔAyu&=b3p1y+J-? <;^l]\eTR?M)}p 'ɏML.NM'4>K֦^o5]|Χ&#kQ?C5_L]R2j.hVt?8zSWu蘿Qm(Ӻ}N~ޔR'qɵwqlzi":NoM)CC+ ]/VRɰ#/]ʤgNmٙ7f~{]m{&ϲb >e3Y~J37kgYb@9;2|%VGEws昫%eSj|N۪TiU+*q@nIJy]V[45^=*ZwnHUZG9+Zb}[좴%rج׶w]nc7[o7T\Ǚ=.(/e|P?z)u2Yrf ]Լ,БU| '~͗Յj-lv'Â[NW"UUFwQ'N'=st>((λ>Zcjq097yNzsoFE8,>ogzdv!*}U;{+jE.7E{vdDj&*UP7mǩh;"}a)uQ>y&;| bur;Ϫ?ucZkM1v٪|9 (cM|T8fy[f:,uětKj@-_W5}RI{_'>Sͪ/ea_D7eW-[ǒY)uf.oiX1ww]_4Yه}\>bmƽ)WuĎ_?Ӥcw육gEY~$^~s'׻o~y/ŋvHqe]Kr]jpLadPUJ3}h~z! +6RHw~O@5c,x۷n WԘR:çԾ/}FͧN,'΄mX>?ߗ}{dZ(7gP$Nn> OZˎ(u4j_А.r]6,ۭ}!A<.5%ij^sWIO]?6l5)eec)@_Ӷ*Kч+[SqsU;E$DSdZF7t^jMvkm껦LUZSy֐WzL:cDA-OW\Z|fzMSgZżiC%}\;UT5r :vޤrڧ^4uUOYЂ[e4H.S'=~5Lz7Ǘ'uoLo t<rXv )d~ExTha]CDz-lPGoĎoCj0:v^[ՂߌP{wAek'3S3W5bɡqU֯Ðsg]U"Ѡәc?/ВQګ%A㚓lN3hב: t+=5rl}*w9d 1j}{r9Iqns{2fnvo]_qhGǢqζ ϵDympu?ƚD:9'c?u0>3qj{Ϊ{n=ya9ʄ0}u6Ͱt Կ4wM]ǫ%֢&kSn]j2ZT|'Phm}j m85'y~_$l<".m o tZQ6 㱋 (w]gZ|-|Gsf~Ӭη%<zaÆs,;S]_7ȣ6}̴ŗF𮞥%!WjnY69ii0] _PS}[(㊐gn-)ALI7떽J&(ǹhvD)Y0OffMrLn:64yt=&@?Ծ@7P6 u5C_;.WwV9OXsfF/[ ٭81rmssugj[Wm7MW//ZIrx?Po~rd]zS(ҬYXv=ׇ'+&UIQ󂬛sOjQ= .rsѧ<o^_B݇]YZ:S% }n y4yc^xu=\MO,;JSDž HƵucek0ܬ z| 藙?Zs"Qrj~̞RxfYWs}Mxmins=7$׋F.\V{P}LtENNo;]o|^i.+BA~{vQpX(=Bs}T?ѥ^9G5ͫfϪ⻥j󠩟܃u.%\,ySj,ww{!FP\0Y4^?5^7^E[TEvM|$6K"2i38V )K^n9nT_VEo]zsogMOG~fy9ۤ~ܨ,(:I;?m_O3%3rܬosby\yKJww|Jiavj]n; =mv>ͦl{c1D)kf%~kOLA _0;\"E7/ZRCum`YyH "u|/`Ҩs&߲'~[MƘTB{k{&v,i#<']ԬW_͗yq:-`caųnWu nT#OŵҟX-li棧wc\pWj^N7\RRt$7 mcqw<W?ɌڿSHm#vĦu;whWŦ<{9+Ԙ2N[ݻwE >u~c6͡ٳ p<mE|}&s(Ȱ8>墦ds]sSӾ[QA/Z5VC2l9~j67SSgx^B(|MUk9=}xx#:i.Ux#2: _~8Q`W>6NAG8wCWJw?Lۓ\ zs`GO/)tq^h;YzB>%#kNN[m=tMJٕQѻkꂬr>gʞ^KW.0|wtM]p_Y(=ǹzi}_=$nqr!0uՇ:Jۺg/'EM_=F;.Cry'tպ볺Q79l^志^Ϲ(n}Eҿ]cAnU8׃mǸץ.bɒu(4G-a5M44z_2bdcM]l984T'G|`yFN'Ji]_bآm~ Xvʓfy>E>51X$MI]Etc`8ծۍpG :{Wlh8DS1OzXʈY!ߧβt _&w(L#b+ϷWt o>M}k[7oMwz~}ZQcr<9& x`߿xЀ3=>AືھYWQ=F~tB{p0X3#siws$pU?U@Ʈ.^׺gmI4{rARؒGZԅuY}޷z!vПݯ#x@lz51~qz@ oV7d?s29<( G܎ ޑvOAF/)ьQ|{|V!Y_7mW2 |R!_%Xga2ds-e:کuj>=lhK 2+GvȿWYUߢL۳S粴ns@D*[c]$ 7Ū>iߤ򅛦-16X7[/UqȾQ.Vqok#c0u\3)p1 .k,ֵwV5)dikz{vVXߘ?;1Ȳ-,Uw?FܿoOuVdxvz%4' ׽voS)_cO pk?h"&G,|SwF'Q0B/ju@ޑD1=}tphE+)kΏ7ڔt/LHܚR~U;ǫgU䵤pz:rwCV#6PRS9<4_гQۥRoPds7R?|S7Z~GQu؈mx1 _p+qR/8㒻ᷚ'w;Ǭ E[vO|zf!jjt=J#ߘƹrVj`@ŝet*i^7ZkVFsy,[=wO֫kKyoѫmjYb/{UR6g,}Η|º9'8WGݼZ75(mOjlWt48Ew=/J_sO;=㏘TyLg;&e˓>ORjة12pQ/q~"w3jM^ рǣf3K*WSخ5PfB3xT*u;|OjJ5{ kNݡǪIb-Fnˠ9zWK?NjAۏ^N{nke e:y 5i#ޭ< JlFA=eT?l0sMd{}x^m]꛶O\{g:7{HNپiA29QW|߇}ߚ,V7bf{*)is jq9ُ)v_.qCL!ՏngBv-s4ZuipR+~n\T:V rZk#O2}8s^ܣgoMΞGLU9QIO^b [td|jTïGtv/*jΫf5g]df?δ`_mvb&g7h whV+3⭌WM% /3Ȭݩ[~q{ȸ̭gh.bT]rSm+987Wq0+N$z}lϦq4p)d 'r6Mi~֑{'׶{µDu}0#Cz.9qh篩ҰYI6hCA[j2"`2O-drMM6O\L˶.7IٲBM]T]֞ 4U̻ Rb|!y M"\xR2}4mzv[fmʇ(y]yd4n^zƲ^@q?0=liyV;j)Y^SSF4u}/voK=7E{Y,GnFyAd>wYyU>Swi]B54ET!K=?|)3عc;Zovm7Iا=1{3'tCflV߰9dvJc}D 5o$q3B,{U0R =?gcƠknb3H-뢗'^}luL;[ռ*c4,^~oI]E~ 6Kj=o˅ӝպ*̔(ez#~}+Qm[Qbqw)3qafV(]sR?'m)NA/{">]čɟ7ݧo;{-*Vloj'OI>[SsZ>pսި^|ޮY5ZSԣڏ9<Éc6I]>T]ﺳkc"J9Mpt~xNAUJ0gEx68KپsiE/r`zhlJMo>+'oM%.E |45?) RǽOG΃+Q\GjsVcBG&|OƓB|Tˬ+OjHFtMjyuށ[N= ]g2-كZꓝmLMtrlJWkWIr`t5ƴ{5{|{KK fR[֫zGԼ&+@?.[_wu^=U%6仦aj>uc }%GQʞ~nU}ɢe]|ѮOEd﫥>O\g;D}S}Ҷ nZfšszL}'Լ*W5{>խKh j^U/M}Cgg32]bn/);P*lo;c:^mZg*xm;+?~BުԘȾjfj_Lv}K]~ƨ-Y11a{~O:=Q}L#ӫ ;kO s~I_ 1Ӿ}|KIE]n~ܢfm>Jr֓e•khǯK_'lZU<×C?ޱzǎ[^:ҏ59zoHBTgyeP-6vhϷYqtʼFMB+??9A >ȼFU?^nn t1Hv?ͨW+?]/4Ϗ/h{nXW? >fڼI>_8$sc(Froi_BPmƁ,N]9ƕFZ)J8bI/?+<؂vmF+}Rb^W]?w2[s:ML^ akL}¼uac:1+Ɂ3K >lq~Om3zm'XS/\ˤ>݇#2VUGRRE}uU+[ޠModUo1\M]CQv1cx6;5uQiNoAU/S{ާyv4Wwi5 Uvyy)ܚï_R~?-67d_>{22oRrSM]sK?7Ρ>F{}k^ۿϻIuS)ѭ }ewByRUޯg֥RzUSɘԝ:J~n#&Q4h5E{}ZW*=&W?2ͺkZ?7wz|ny_GYHͨGѐsomPJzJRׯO >Uj}ݛ*jZVk+Gv/!ryE[?prjge)FQyMʪ t߷GӊT%bۦԼqڶ*M.vvU򖑱mFkRY۫4:7 L:yi9#ϭN?L۠(ӳpgTU7;PR|px8cQDձ><ҞO,TK .*\*Wxhcߵ];?/8CgEzÝ8Z 9n+鏊:z7n}0~KGstДjA6u~>ϩdW= 'Ϫ[}Q3֫hXwe6v;B@׺Oqjc7\!a%DϲzU[8:!,YO+w>#>K{kuYDbOa5W>ώUWS{Hwt6]5l;̏OFfjz~Rs^7P`ۮ+pAf~{Fd2+K(BC(ejtAUu{&s%?KNCs7l|,bs{C)+b')EtqZ:~}vr OUXQKhBk;U՞ā?ts wlRSTb5rf({ckhׯƆrJeupCJ`lWzvG;\_넗o-4k9g_ŮU;C5y?Zn{{_?]E]'wծG09jPmnOngd;d^'!l10${'Fpԕ[پ=ͥ9RundQΘGߞi̧plQ1TeUhzݬ]W}td\ϵ/r^]Sgh׵;)͍l!ȵ̐& ,vqAt ^ې?r j*<`J?}ܓK_iW>^+پy :6"'STzkV&wx=ST̋Ӽ)n6bgaf쿗^{?/}Eɛ~jv|G9icD"ZfwiMi 49jqoloO=s<9+vVˆ-Ģ1ϑq6+㭮]H1|XV3}gT1mULܴJ 5jB":VWk9nk2LM]x9>xjlnA~9V$VM!ƶ4xR{+?2JX`֧/XQܹmo5gی*'wqA~;wiNzS΃+}Oӥ3x=HSǏSzSھxPdGQRmb9e\\pj¿ڥi>ܒz2nNFev&Ul3iJc)FlmJu^ ~B Ruum{`nERQJ-Cקwў=>PJlʮ[xy>-*>yܽ>*;T:{E%{~W#%WC1u:2͋˨}|syЫ_.}ȫBlRƫuFUtثie!׻?ȭ*.ұ:ss8>~zwN hRuE0_s bo ?cjö_Byyk= nNsv'D!M{r}}۠U_νgN>_ɯ66p`Z=,>YG?x{G\"ߴ|}" CൕٷnzZ4z Hڕ &9W\,&As}M:Zϙ~1'(csk9LvtՅ|z2,O=>'s7Q=zj8zWh1Y%C,uކڡD&,(}ֹ=v RrXiln~i]8՗ kKӋjtf]A߇ZVPlR~T/v%9nwI[M.us4V9NۢW]tR:o_;끞{xUBa:C͒2kP]ώ/VJmLIn鷃$}_Q75eǸ Tk@x1}6b^^wc?+gqP.rRsl}j^sŽszs_\AjgfTykm3sINl?=R#_)P灹|U9tN!=ٯ73<{_/FC]'^GE0VJxdnMlj=TӦɍ2=xftRٶ,O%}/oZUw=\/RlsRn#g0QΔ~*֠s!Ν)LVHM -)㌑QBM0bg&'R\uΖeQxҷ|p;n=|tK{k ij/Eߴk eL~+E>0QOSgTN ؍'K{*zv^Ug nPH&_#):g#o].Wa'ܘwJ_ nl0ָhUv\n{${Uk_oEi{-\9o$-0@ޥ:_5s}pސ*M+Fܙc}W/])9E䖾е8}:i7{+ ۶[kϗ1Go(lyPnz-slG]J0m,yzh^[;S CeMߦiߦ9e5/^bǻj\_ܗ_~֣q4nm5uU68uJmk?wr0Y}݋:k2ݎ+a,n/0ArN;KI~b'.Oj~!Jt_c(j66Mt>suǯJ 7}hD]h~{志s.)i:wSO-=M7yGi~qNYF׫A~θu=f]{?dZ5[7Cp=M]Ҩ^z+b(Np_Ol~dM1·k[׽J\=oU 4oo_{C/OyRj-{K]>\3_.0f˸oPz̠nI5uTܥ>2u|۬ΫVi=C& >a?gPNѾ3(G))AƧ$:q?jƚ:ޖwn5T#*?~#B5uaa )&Xb!;9YniZ |gЇ5oh=lUʕ&eLE=K7AD1ǫk aL<Y=NZ_īNi9B)KOfzcƶ~)ach8{Zm ԽB1lyuxh ׸CLM9uMލ\L1}2>/I^7{o;G6oMi'н쏲[+{/rOn5?3[7n77ѦUL6_i6.[;zSŮ|^5rpWvXQʲzޞ&ʹ貮jO^ejcO>T)أkmwN~/3u(D/z!^ٺfێζ9H_r:?L|1Or26%kE_=ΧvV})7ǫ=7ߴVHB ;4fAU |4KaƔ?.m67OMu;Rg׀iN[Tј|ܭ=o8j9`wpwL<==P71;ʴ0ōj7R͇0qxP_ )yFsry{?jYΎeT5b͙KS鷇IzYgFi|2UۙY?S#kEރoSŵ5oK5Q߷|Gܪ{Ibhjm'/ʺ'k]dk/~ʩ, vh:_wD?2ECfSw7۠ۜ_M^3=>&NK7BalSl(Y,kG{L6B㒎ޫQ=|(wbJVfwkvKܸ Rrj4F6mݏ,>tȋ6PFw=nZ,̳cRl!(PٍM~]fՐv<1}\`?Mq~֤v(n{[9eնOm"5u ?حO'"s|HG^}V j{<^:B]!l(M$l՟ڱSG5  Y7J7k<yϩM]6o4%vi{=%XZ9 zP3?|ʑݖh꒦-Z|J|A|Vd\>!ۼO|Deos[LOMG^}fֽ)yGfQٯC.A(hSSyL:s[HI]8B_٘VKgQҊ>b?<`A<Ƹk=vIe6XvU r.3x΄?ٻf}^{?}܇'YőnCϷ͓:|DɓWq 9>~c{)ʇ;KibA| kv LC7X+~_z/)w6Jka3i#^yw۶Gh_sjk0ޗ~X^BmM]̰/>;V';q`? **c}G +j6-._w\8RBJ60]Xٷn^~a -J馝7gkdSɥ5S[AV'JCkVx rzUy#M^i XEJGJehҵz@ٱUz3o_v(tք?^isn0uq EulNRXP/js8#߉o;Dታ>Gj5 9AG]WT>h 0/סٷ֔x?cQ#OTT/#[m(je^'sZK7J^E>~xug}V猬{y w`0b3+_t1E\=NM~5bQr.pBğ.Xflկ{UL"kj}ח~;L>zӽndrͨ]-jyN%'$%u2}\׭CN|0Zwߪt&0N]?hmuInP[S2{G7<j2Hf<Ra0F{Zm)RٟK*yoy(5Ng=ɢJc_ kj/NuF.|΍ݡ;zV5ٝX/n`w^Lѭ'%C$67Q*K.H-UnQ,lh7:J|vjEz[ZUa㦑G =1goPwUwbNjoe}bǯaKs*'K).j]o['*FV/ϗqc^{8Zc7B?f[,y7>6=،?EʘxXpXZTFuM|0}2c/CҼ1P^gǯ$-+qe-Tρ-CpeoqRF ^PMx *䱏8gS<\jB{lhd}гDp=BOHxnPisDkO|a{`)cFU5܎neyS=s4?8Y#S\-q6.ub7(|'uku|\{:ybW9 η]:!3 M} l&=K=#00*\~_Bb9[(T9s(PL>nr2Ak~.3';fmpWGLO}R01:h}).3ym;}i}ۄvua{ P79@am46~h9_ :%Q̖g&<;~GU@͓=9W cfh}R 0zFx4z1=vbPy4;Eraku)΀zL6\ N隗z}Iܬs3{T0i^vg((sN%8E??^*fM] :^!T"[{<+ ʹ 7mo.,y@bSc&٣/曷^͙1sqHnA>TXQLwcJ,9ׂ7%~PtU(:2sJ,e2q OP@336T}5gNSpRz=9Hc8h;5%i-~q֕Ч`.=e4ޜӰg\x 9@{~Q ".VrD|#=jq03;o`=jMV֥) Lo:փ8.ym}-ci@9M~ ِ=07v 4]j6WL騉ξ;LWRT։=U'{Χ]{㸙Qq߼׬"d8hUf~;y#3Ζ{pMӘ_1'gw39/p6~%Z7'/Y-܍r܉8}t Xϓ?n'ZT b8j~bq߼`{ϾSϿn֟ 8пo{[{_HE{{ʒ|5I{߾zԯs9ugc|?Ѥ"?pv5)Ņa5Ql?fZN{ݽKxpX|,vt#}v8 :+'ѾE}g5q{zѶrPF7-+{Mp]ޘbgD{v;8YN~hG'}L:JSl0t|_ׯ(v4ﻀuu6ڬd/@3 If̓'LC*&M-NJ`gԖ3C<L)B_5 # H=0זbgy$sȯ5HPM? &cͭ_";fX̲Kw7CmkKT)ߍ}zS WzǗޫ~ӯWհ-*5y<ܵ/*r*!ȇ)vp[ Z؟=b:ζxbh[x|廨rf˼^j[Y'O#@ߪjQSTo'>oظ]꿜Q\mP?:JoeF5/S챳{62y4q?}s%R LMWi8_@nȒ;x.Ԥ7pN͢^JTN]U&@x+/3렸WO^sjhЏɱwi30X $+p.[Wu܏Ho4"^kP/-D\or Qv".."N);x}51|gF*@gZ8KE9iϝdjzVw~e^mW2sِttz Y.`.~S}@) 1v8q< W8(0S1LԮ@ӂ43"fV6b<29:{zxF\s+qv,Eޟ|B90t1& z L3? A݃B8/Kauq)fI)487fJYeK;+'hecWS+NINjhi }R+l?k)vwMpE]dmq Xg+UqyEG},p=uiߤ4c7'BPA`-W8+NqT L`Ʀy8>TgE3UR6z71-1߳/ :졳@k4LyJ%~{+hp8k8xȟt柷pN8Z}.eET|]2_ -!a_}xoAl.\m |ݛUzAU&q<'[wA WХZgx(~Omn>nGOݼegiii\yIy?t;rԸ͹z4lӂocKt 5>9 u`ާs#Mh&Ars^Xx?-,|XCv=;?pFi.=' ̾DQˆ='S .9#f uҹdC턔ƘDA3:{o;]#>)ߠ~![ No;>HͤI]owNԼŕN},.|(kb!pʪ[`s-B4 d0i/ [-5-c 1MO)vJa~n[Ұ ށ}WI;{7/8:八Mjޗѓ|y{5]XtݜoZ?wM0o2^:A*Mx7# GL~.-W٨rlI;tXc۟ }u|*l~Xv-5_DGZpop9s`45ZU M/*g:N΋bn;O>{ 2Tl'w?g [=JMv7wD۫P}:hm.Zo`Y| fnm|xqie2jWN,OF=NfީxZ 07[s5ڪ݋9[+ϯ,_[a9W ;{jiՕPz_憤yF;T㮖csϛ%.7]Y@?Ysyq?Ŏqph_ Ԛ)0uod=8;ifZXnqv8SG MׯިL_e|[Ol; Y_ 2n7}xߧ(Z[cx7'*ng}u5C.;{t=pU7=鋾kAĽ{F_Zz%3b5<w=o7R>=S?gQkޫKa yd=LKKozY@_?勦owfsm韊`^緈#5N0_l /˵^|ߏK延J6YL&Md,"#׋W6?Ͱ-7gW⸅E$nD {.'+ HRZVx{KSR҆-'E =ǁ4ׯ] g<2ucQPo+M㸙WϽݯgqyzE:^'k / vxYY~׳O[o>XY<4W+eI|ة wb{@R4ƊOqp9)5p~]TR4Snد}sdBE,mUQ'g&b:u)Tqh;{_8 =.j"/<3 QvPJ5FOIC;y5y#x|߯Kիހ|a\ﯯX2U=[&?WԦʉHbty~ܯڎpCS~ૠ1}ł8OSrl*3o.f$[m C0v_n$x˭r}F]åa x:1[gXoߗ9U pKʞ8d!P:]߆30[~kYq'5^C8.Y˷Dhw*q>,}1sxҊ}Ltrvػ%L[ J}ڼԂDl~,2u$-xuH۱ W`Ͳ{PJ+6_DNCi ⷩk\w 03 +eߓT/4x)H iB7ƨ Q N+п,;ۢxjKd_fo*֡)d<+6\`8e;>@[#*#d }{qbg3+;8$l26=r?mۼ믙`)Y{bgdz-`=takەqWbg; nM;v-\-@ʯm$E>}}Z PMQB1-k! G[jrTQO?|_z0Zl*0,Mon͹Ҁ|whawl }G{726ʳU)v#3'{pmZ:/ ;0t\l}ydwh2P|hݯ׀NPC̲µIKs#kWk{GSj}BI3wC XSdsZSrj `s o}wp- ^NsˠvDxS`5myu,BU; Xm,ͻ.vz9$&w;-BWg/z2id͜IF;g* Lɬ;ܼT."ͷ9%`QHCpc 3f%8}*:+于zF˿d(D-` UЖݤ1(6))u;+qCM(M;^* HTݭVHV]t6xzh6Ɔy'2{W%ë_opU#GJ2v"O&`g8DwvZ4pF1 pB]6ȾIJ8F%-{3ۖ*LqUOyu8>2! Ұ\ʶ|̾w=}EYFub̑iZ,N]޿6f8wxc;:6Nӿ[8nf4?!Vb3vg050s,o9C O9pߏz;9~oPSd ~0Tj=eeq#)w/Oײ_:)?~> Y&D/x>d7~ .7G2sU?rתJ7u҈ KOzƕw0%xҳĜo؟[}Uר;}"А,O/U-0bBΑn?Bdxvۊ㠆/ϲ/m??/"U6'pJuܧd۽|~nkaƥU5eg4m`~unU4 q~F~|:3i[_j I{rm>L2<, 8+Yz!,js쥾G5uzBQ( Ը-G_{11IddRfu?X>0zi04ߦ#++2 >7+3djfjXh=:^^L9!yzk۔UE ttqiE^''"j/CN βzZPt8h۽GpH*KdD\ Z=gR?7[~K^*)#jNJB}I/j P(2t -OUSrss3h^0.ζ0qhԎgv0K }Q02KQ^;IhS˩N᭳*&W+ACh2 ycDvu0ϖtZL39v( rkL]Ms_\ʦ`|Os=>_X-?1ԸΧ33 땗ڶ⠵Rd5^lQb*9 z Vs8_QoU_u`6lh ~#x8<+L\fww,zjzG]_E~ۻ;M7}E;g1K͇ڶ[ᢂ͠8x<5Q-}]>DtC*p.04KM)v}'zV~%ֳ tm׳9x_sJcŁo)>-ΚL{s%o95 xz76cUI#3m;θ=c)0]I3;r#ǁ11Ɨm}K3x(cݺTĜ!r,^ P.]"w@=D-b#amtZĽlg5=q,*>,Ry+q. sg,BFs$80Tֳ~pҞj֎YuwAccAwb♹ 8x¥UԺm_[kRShF-٨6( v$mg v ){V-~r~}QO- }+e O|) 톰uda09祘 /fc?eѶx*챫;D${[tրG_q=RVkC'%TWp,Y`Zu}ծw3|>]msIKzK_q;V+NpxOe4*uo〗7&-XB({wl_ ~U=O שLjo͒Gqe+xh]Y&ɚ&~SQ4J֟lC9M|W s׎%wsN=Sիq_*κ/>NN&Xac0^ʉx:]r ֤DYw-0bVVʂ4~Հmϟla2e}gkĉ<?ep\Nat=`]B&[}OJ{  O߼x gx+s3uTݚ&^K:Pkma0:-pyu}+W~qk]h]ś69D3ǻwx^gE^mQ ˀw "<<W ,Pz%כ|yw()8{f=d~k+jcΈ1H5c>Z޾U`<,^##TڴI!壸߽fxnvN\q0)YsUMU{F 7\w:܌f}픿)IGpM[Y`-pLr=l6?ޖvg4^|eoѷfC>"V]? :V0޲0<~n4&_/߲?nɁٌc/>S?pA-p՟/n,i-Ќ3e 8m;bstIR=\ O]4,m18ns`{Fj}j`܊AFNƧ@QVsNu%S_S|B 0eULW|=V*3OsVoZ<>:m|>_+BeN cU \ݫez&nv9%Ϸ>ِ)=`pVY?O?E`s,j<h>"j7͜4)̏; ʛ4O^[_MI wkֹW3 iEm @m~SjYpT{Bw,V._A^xW-7:vx(<=j_ |FD`)o{o:?^A-j[iݞNa-pZ1b~ĕwp#s"`godr:EԾw vyA3+6p6Br (ޖF=og5r<[2tm?;ױn?nf6oj[lVp*~vo i26t}[=K}ϫ檆E5 E x?1g'3?@A8{ٸ78F?y+OS0^a~K]r6q"'l+?u:Q}O4T"6-YO{lf=;V;+iؾq} D_4>?&ZeK6mߑ0 ~}JVJM䑰J꾷> {,3vI9 #,r&?*ojOßkG(ơ*>Y^2p63km xm{8/JP~mH k/9=.~ʏGV׋جG@'lY'Y9MǫyCOwpx! {S"5ӑЙ;f K[֖4~{ m!~gR*Zf8g>СSuvce-*<7 8s654ɔ>ㇶ}{bL=:yvSP)Xϵz H>#ѡ^|X9yp`+wc G?'u~4OD?ʲQsib ;x^-tԇ+2 og>:BI`˖Nw_+-Hy_ N#1*D}$/pѧoJWoavϨ!j<F籺epwDYNq|Ty Y7pwb~9x~ٴm9Ψ;R&}~{ ~voP7T5>]m7H+;7a}Bhw*M {w#uɶ *NtOx'%e 5WTG8٤S~Z G:?ۂ);:W?ֹK`(N5I1&/^2q~x|oۅO~Ew~n YJ/cu8/U{! fGWY[SYlgߟl o5laިNg6L _^i-x$ FkZS⇛s&={ħ ce͑VJ~x9!^7UE-| ;cK !؎FauRijݧϯ͟Yח{s]@b ۂ?T}:VfWܷ=~fw}n6tE<Λ5wu{rI/7?Rxp~'==R6v8oߒ(; hH{6NV0vܓz#R_%7E;KEqj^Js~AQR8~{IUo(lY" o./DQLsRdjy^јhm&k(҄*sr(vi]UYx,o;Ɔuu~l7:+prgFzMz{r6m.p(vfV?;,O aMןm:>gF ׵Yl':9]*%LWaԸFiþ|#oqy`>+ K<,3|>ƃI*f&\ 6,L/ay*S ~loڥ5Ypp~ɹ8 f`a0<{1\fM!=olh=v\)ƁjƩ^N]!MљN< -$qޗQUpUX_yUƋ;t45_4Pq_M7^hӿnsoOWm vNW*Cq=gp>kzɺ_υ *K&2xFqt\qҌY_ēq<^R 6N6O,k ^k&d6 ?03qb՟A엳'8⬉Dw}\y }wCjR6i†Mn!ĄÔ/ w?9n.m8r~˄N|o%2Nvvkp6i-[IJU1/2F^mfwџ=/y/vg~ϑ#P[zq G /}줭u1ҽcPsOǛVz85Yߕ*)3C ul˲}OrSʶbnU+7 ?,E+Fӳ5xniF --U~ןu A]'o!p+>̡ƇXzF'X,g8yN54rA{3vBL.o`OCoX࿻ p@֥>m_ 6_){u̜?A`HW N)uQ0x1Z}Zul>NZ00?ށQn/N~l'@v~ 0x` _7c izdi`/'Y{(`;TWs{_ 6O;}L*U;|]Z$<~0k!NZNjrs1xr5OE.2dk`c]~N[H+1?tQo+p)a'N|?[6־ܔmdKK7餜./~5h#ެOa.'+k Ը֕KgOhMBg_:oNv`i8^U~hi:Q|9h\̗ړQרi-kŀC`xy+'Cs8ĹeP4,+n}y?_x3uqb/x6Ƽ+߶A[E[>; 7;N+OW <֖-jd3+nZ&<>7*4p>@c36V_ y7p^ȣ5'5-6[;G4NxKoSXn-_jvTijC5]8}T*k_ F9x}nѳ2b-1)<9|Kٯp x})|p[腝;>}}.X*;^>m~!`/t) +lElWv+Qn+ 穀4<(֍wcCa7"N͹ۇl=Ti9C@e_;皛Equpia ]ZB^ʯw+%d$Lty_GlyHS2L]}uCCyi L3q6t#XdL8v3^)0w _G @V{.uS[e`B_xɂ]ٟ^0(w χa'}=Y2}xvW-}d}8wYuewaQlf>$5n*ճǕ/4Ōl=Byks|ELXEgJ,`^"7M\3r˝QR'ג%hX֊)cСՎFVT ފhX]#`5OU{ye_S gg󗘃XK3^wF>D qSSB]5=eVMgP?fltP_t 7hfkq(g?!~U\W8ɀ_3e| XHVnFJCBqVz!|`/vh.\CSY{'Ȇy :m سNOL_|7] f" )vũ+Ķ_4(sn7:|8v?JQkܒi_#5 VZN i' ^ӝ2_ NfE+d7VU?7l@GG}6m 6-^b':S|YkyZ9 O+,m/)rذ% #Aw*jIkwa&p<%faпsZΎnp-8oM=u:|XPLϣة<-X7'jt`_ >Rֆ&%QWk9;|5=Ώw-pS!7+D?E|` ]L]p ORLWrrGc`v. ߥ ʞ~%޽̙qL`lKzbGkKMu%[Ņ |VuO'DŢhCLK9,[:o6xȱӀ|s"'2`lHEg,0=NLC7pϣPd^bDz_+#:F^YMߥ9N"#avp}wʟZwl/:vU `[q|`;s훨7q~h`*|f|b[\Wx؟bV:f󶤁{#ۏS)do!M_} v]UX2C._yK(lw}Z3uԹOak6&n&[K|B'B=Ӆn!;5uwCgI@}W#/DR ](x؎z~yֿΩߛ|0>z=LvOy-bd[W3xOUŲziQÁ|J= X0:ZoO_bvˇtqVrxw?t[Z렘JۋM.bFZ9K!XUzN;FU?.14SN=YSRޡ Y8,m;W{eyӗ;p7fXhx1gt>`+*\3ASfsch5#8S*o[la'\|Yhq%e,\l8{Q[c xvq $8^duNw#mE8}K ,Klwao|o Wg-?qy(/r=plb$o"5l=SCSI`bꢷ'9XGJX#o'}EdV|deo5X: p^9=%iz^(:"$m}nza1NOY &:{8=`ܗӯE`cp=? :munb̗pd<7qyLL?SbUM֤%魆kpmm 8_Vn6U-v~w81-5T>p7L|~Z ϓ99Ϊ%򒟁д2y*?Da6 ; 49}0Snz:B㭥MkMP-n2o¢ݚ@UQ_ F{Or)v*{nھq) +4O4)vfs2z=W5]dbMGf7oJ}nF;`Hύ줮;hgN]WN4\w;cM?Ծ/O=@oؠυ~FI. ܧ'>qlE#3aSå7߳bXf鯥GA7$jC1O>MVźmZ;D:dϠYMViuM.0 pC@ ?@?`p;L/,ypw0x YfPx7y*/oţgCA{5ࢨ7.ݙMcH8ShÌ_n`@\иh&pX/}sa 0 L٘dyZ>~j5gei^ʿ;Ӓˠm|uֹF`W+SY.߫ 7<uZ6χݫɾBzуCjK_5_nuSwJY0ċЯ҇3M0.E: t~[ɇ{wC[03[ jP?~˳fϞʿvw,(vfnh WơM`,xUik78 T u}Ĩ~ɑdm<lݟ)w0/Z^ eWuS8#[URmrn+*sb>c//_ɴRL}݀@oچgl_ms"ն㗷M\Y,0we%LYƻ t\*YnkU-h^+Ԅ?Gꦚ^+&FtWb{p7!cb_~i`7w%T hռȜ}52 ӏ"{c$T0 +z`S\gYB{bÄ2 Tq;M}̧o.MxTdN4yj̥8OCxˢ|8>&|-4q8ذ;N T3mқq^z׸vR:YT`:+3$NxM_q]~BX]W5u.a-@,SdGpk 0d3ٟW^"}H-@ꍜ`_>_ {uB' b&GWxLF;G/CeL^v٫@JI 75qG2S:uI&U㹨n̜Բhwrv8Z%x:.(}k 9MagԬYVE`d~KDMX,yni[%&Ea'm[YS}w/(Wn-cb"۟|!;fiڑ5Zj7ӌslpse?7ebd7k:%'w+L}~R~;t:SA3yb DYy9_ǃcy'c=A3wp"ߌ]xTZT} 8 >SVr # +n)\q7`^&'meƱ[ 6!_i0Ԟc;kQu_gɇ&[ԺOjփCSc¾sx1/Al{v#?S̪a:0YGb/[o}L2[4$ %Ig_w%&;sNNNf~ P9$-j:`\J-n[XqK`xZ낾30 E:th+iXMbӆ `yV:QOoմ{מa7s ة^s>؜5Ӵi!E:C㸝`y&_V' $ĨuZI`u9A[ui 8N߆Գ:#pP N?XOLjY-e }yi2ꙏc$_eDiN|rOnδBᯨ;U-fZ~禚j ₩k`936^M]<nZXOpة?2!\}G jX6~Ik8:wtΥ.LcGR\;<.1}](unقŬw`ziJվ;]{ݷk+ SLd/sKd&~N.0Tc<lb^ ;~[:]u(0_/p;w]F}g uP B'V<4A~ohGu>YÒT,: ~%F3(vS- eƓ0uʱH=;Qpp _٥ϵPیU!`aOPff'H+"̮E光wQkzju00?nFo2( Q8׵vPyq:Ӕ3-Swەs:g$f͸13k:(j]5LԲnH= #۴A!3O95:Xnip=e)h.AZ?m#kT,-G=uu%Wh1/~zQ-i*^E!G(VyF?Ow>m B='Ծyص_! vO]:٤(XWBdMϲE.}dW&yz?ꓘ8) 7u)'e~/)wxMyUf Y?_e݂,|^n9F릜] a|u8υrzcw-C`[yl);2 ~],{|^rF5u-۝n Dn'ot}N?џn!G'44E!7ܿc?`*Z<`u91 q8zNފ߷FDIsf\oyCdO̴J[g_/Z_f 3nnhWc\Gk*Uۼ: nYus|W}%6&ygQ6U}>c% Bwy$·̙gRi(fBOdcڴXw˹~T+ <}bmѯ/!MqOJٸ3}D+Ɠp\is? <9%͵c8/#6҅Wp!ڻ\Եvm1ˇV?J֔RavD7wb Jp2fx݀FnιRj{ֆk)z҉}c?;dʍiSR]v9O2gt} .]tGzQOm*F47slWK+x>о.wʝF~ȴVNjPO_S?R3_=6"߬l ˓+9j4 p]9 &UF/9M><.22[)e:Z}ˮܙ_}:wHrj%\6=j}hS-zҩ2 @3㑏cըd{S6|dD:װku/(rȶ1Twm@@_։?O6+inn0_~ &.X~]4ۃ) N8^ggmj7] ;SED7Ru{ۖ NWu}KK?gɃS ֦0jZGN.vWuA֥/HMĊ_!j|37zWљ.߯[v QH GSR(>d)_ C܅Z#O>f0"TfQ>J67 gª08QO0h0n)(Ў*(vf/]4Ò]`/}X*躪}6a}-WEJRO+m>m[rZ-01!sCy".z,$u^{j|loRk}S/O .n `xyi7#eW pm`;Mw^ʥ֔w!j{&\ؾMqG];dZಕuE3_Bs`^STbU?8%.Za,l+ΪREi%0pzӧ, f({ w_yԶn5εr2Bmw?g .e_j ',t36^IznjzpJPAެ+IVM4SF?#tQj^՟.*7*Q1ZH|D~k슨ʻ.Ӈ\RM^Źwz|mY4'asji^ஜ}vez5e| gOmh/ XU.sOP'd^w~<~H R0 ~KdKp6̡^XCyha+bp9MQEKۖSkYr?7j:p?l gP[)>fwj>o}U!_,4^Ί/"+|ZvwoM 빎8+<=v#Ϟc$9s߄یS!ɰԳQ3ryvOӖ3gFvK%mVD~=Z_قR5ēA?Xt~~ӈ ; gމd?^ѽs?UcFWU~z Yw͸tEYri Zv*O`:ԅ;2_Z LpN Kg>qܝz1Ip~`jjz } =q?@Xu!Ai.ε5 [bɚɆrlL+q'[^iqCg耗شV#Wz檝.]I f8fmT78Nл'Ĉ?܃agʟqSҜ/qQlO"zMWpky#g?LXN?tw@swZ0]Ѷ8O8QS=`ݚ6i4?3qتڜr&),Oy8hץ*g J3oʫ[z_Xi7?'~PUxqUM81J[p!:\ib_~5 L8 [=bdž$DW1g![#bɆ\y˽.?㸊Sz儃u#M#8NØйjwݡmv G̰&m>A1TɜOw K`<0p?^֗*<Z=s:RrT#^Wom2;Et{@t 5{/N1G[AmR:~_灙2 r3~Uoq*l Geܯ-q9k,1>U ǯe}Zs ʓG;kG 3ByW S{+N,uu6n5wl`t8^6*㸞qƱY><s48Q/^MO:3T%XolݼWdt?{3&f|:S7ҘCN_Rx|6eUw&ӄc×$\=xp>]Zb/spxP MZ}_/E8?S78N-Z+x3t>fo^p(=ru$ ]i$(0Q_Hc02̝ȗ*T0K1+]"X'= f fI;ܷQ?cl`V̆iεi_jTRgLI[])v&Ruڐv)I?#S[鷥`Me/M 6 O @tG``z~"|?ׂJOë,fgO9NrL5_\ۥ37kS紊Gǩxx;x5A_%\_t[̟n`9xl+jNBaY=*K~o$̘W7<6ljӬk[i0,>{{ 7ͭ+%xfe箰%V(Gl4h̬-Y?v f>7+`a7~ݗʊY[J0Q179X;lYKk5U>=ן΍&n`9_vA*:`'}Qz(څ*69Kg'CMp+:za5ܙU?6Ɲ'3S9ވ' TS# My }PI%zEBwD+'; #/isizJn3/C+'eܭ .]cz򇢓B256*oӓ.Z9:B vbЮT 5Q/~y ]O+?HsM:-o {s0,!?oN^E?Yeد37n uNMD΃U'c);4*pTf:Οr4`.dNճ/{tn|:>hy`o<>,Tm6˜>mm%m%8b绶 N]]5[/q8sR8/Һk֭3U:x]o!TSc# 8,UbZlKۈʴg.-\չY/"/]ⅰ_8`b_UM,\2Wom:'g1q^߇_KfÔ+.8G;{= xC18߇e]q4X,h j7F¿qd"`M)}ڜo+ٻK17n~? AɣjxNa^z[۔k믧ށ/W:ց} 0jqt˝ va3.1U<unq;ߝb`D֎Y+ SEEepJ~g*4wNt{n?4;U{-dnJ u7O/N'TZHȺKդ||=ϛ੾Ns|~f$\&D87h}Ih?7Q'ɲ S@ m d]qqp跺Ev^UOG80r4[Q̥ ENi:_Xcg /W~KV+[;)ޝ>M`p)vf)JO9콜#' 6G_95:&y }E`t=F%oxit@7)d~zs) Eh zi3ۓW A325W C.RmJq;*^pln}f)ؙx[l~lw=&{-;cty#N>JzF}C00S~ErA[$>7N'ԎZus"<75YQ?7' ~ :RbK0\2F73N`.MM$ǩ?|"c5`#մW6Y d`=6.V⏏^^9.d>Mպ-] _\`n>?w㓴K+I|bwBDp%vyD@yyyyyyyyyyy.y.y.y.y.qD:NDDD:NDDD:NDDD:NDDD:NDDD:NDDD:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:C:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:G:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K:K8‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),D "‚HaA RX),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!C ),RX8pHa!G ),}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx>}#Gx?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox?~'Ox_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx_}+Wx߯xܺy|>7<|~_νo w>ysyK9ƞ?v|n?}>hˋ/./;=u8.ݹ~qxzǧOshazam/data/IMGT_V_BY_REGIONS.rda0000644000176200001440000000112013762520614015743 0ustar liggesusersBZh91AY&SYO x*` @z<儩Ƥ4S'@=O z44d=AFFM4*L h4 hP.T] EZ(fT ئ[%*~GCPxLQF\*UY #rYЈ^L1SFBee4Q$ aKU0r*ۀ/DeOFނCSZ~t&8"{J>ɧ&k~?EsO+ Tm10; RuRQj˱z*@U$`+ x+K,C S6=)F(nљ!g=?۵ _;0XTԝ JOH2@e*-&Z( h8)דraZ&Iq>|`A"p3jI9#v﫛@IQBi Vacn k[ܰ&DqP,XPȕK" Xg fN{1P cĒI$I$0L-S&;Qi "&jNp^(~өV}/02)Zw$S ~shazam/data/IMGT_V_BY_SEGMENTS.rda0000644000176200001440000000101113762520614016061 0ustar liggesusersBZh91AY&SYqmP @Z]`p*fjSh`h@OMF#Љ4hi&& 0`$HzImM4R9P66&6]3 .uc"aZ(76:aH45E@HHb@0&Ђ$D6"G,tK-Mߙm~B!Rb&i> Ciz<rve^TeجwqH- "5FN_Xx L$w} cHb&VO ?J9$+?C0]~m]?zQBSI(|*7!hK`b6OIU1:C=.rQc \)ѝ9hdڨrl7"0_P17XXY_R`={qLePPW6y{-j o.Ahi ?]B@shazam/data/HYDROPATHY_MUTATIONS.rda0000644000176200001440000000170513670240241016276 0ustar liggesusersBZh91AY&SYl"\a@/ް@^yUUHިi=Fډd0Кh (BzTb &L FL!Q7U$Oԟ~yF@d O*UOS L%12 =M4M=PQc!$.1Tm0P6`E@]P6 ljPk$FJ,)q"qUEIHfWL$3\ʵa R/m!_o  @E&m^?">\Z=:4tӧ=TAP*F2-8CۗB!TUU4jTIQ"-kJ,"+%ZPqpՐi I%\R嚀1 1K2W*YBfčkZɌUEU\YW sՏsxmm'`  [$msxosptˊ-pu@{3 $PB"0H^>zYE() pmK}xݹ $I$l0`*UkK6;M4MI$ՀVYZֵ;ZSfak$VX `V_iu8&UX `*U_ N]&;i'yI$fՀV_%}K:v͖eY2KL$V7qqW) uT!msx m~kJ 523hsp e#͟)c. QXT  1$VJUyB;;!I]o8rrɼ ek@ y@Ei\7(+AR9o8ݗτzL w0QO ܑN$,"shazam/man/0000755000176200001440000000000014472102727012316 5ustar liggesusersshazam/man/plotMutability.Rd0000644000176200001440000000444314470664106015636 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{plotMutability} \alias{plotMutability} \title{Plot mutability probabilities} \usage{ plotMutability( model, nucleotides = c("A", "C", "G", "T"), mark = NULL, style = c("hedgehog", "bar"), size = 1, silent = FALSE, ... ) } \arguments{ \item{model}{\link{TargetingModel} object or vector containing normalized mutability rates.} \item{nucleotides}{vector of center nucleotide characters to plot.} \item{mark}{vector of 5-mer motifs to highlight in the plot. If \code{NULL} only highlight classical hot and cold spot motifs.} \item{style}{type of plot to draw. One of: \itemize{ \item \code{"hedgehog"}: circular plot showing higher mutability scores further from the circle. The 5-mer is denoted by the values of the inner circle. The 5-mer is read from the most interior position of the 5-mer (5') to most exterior position (3'), with the center nucleotide in the center ring. Note, the order in which the 5-mers are plotted is different for nucleotides \code{c("A", "C")} and \code{c("G", "T")}. \item \code{"bar"}: bar plot of mutability similar to the \code{hedgehog} style with the most 5' positions of each 5-mer at the base of the plot. }} \item{size}{numeric scaling factor for lines and text in the plot.} \item{silent}{if \code{TRUE} do not draw the plot and just return the ggplot2 objects; if \code{FALSE} draw the plot.} \item{...}{additional arguments to pass to ggplot2::theme.} } \value{ A named list of ggplot objects defining the plots, with names defined by the center nucleotide for the plot object. } \description{ \code{plotMutability} plots the mutability rates of a \code{TargetingModel}. } \examples{ # Plot one nucleotide in circular style plotMutability(HH_S5F, "C") # Plot two nucleotides in barchart style plotMutability(HH_S5F, c("G", "T"), style="bar") } \seealso{ Takes as input a \link{TargetingModel} object. See \link{createTargetingModel} for model building. } shazam/man/plotBaselineDensity.Rd0000644000176200001440000001161514470664105016573 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{plotBaselineDensity} \alias{plotBaselineDensity} \title{Plots BASELINe probability density functions} \usage{ plotBaselineDensity( baseline, idColumn, groupColumn = NULL, colorElement = c("id", "group"), colorValues = NULL, title = NULL, subsetRegions = NULL, sigmaLimits = c(-5, 5), facetBy = c("region", "group"), style = c("density"), sizeElement = c("none", "id", "group"), size = 1, silent = FALSE, ... ) } \arguments{ \item{baseline}{\code{Baseline} object containing selection probability density functions.} \item{idColumn}{name of the column in the \code{db} slot of \code{baseline} containing primary identifiers.} \item{groupColumn}{name of the column in the \code{db} slot of \code{baseline} containing secondary grouping identifiers. If \code{NULL}, organize the plot only on values in \code{idColumn}.} \item{colorElement}{one of \code{c("id", "group")} specifying whether the \code{idColumn} or \code{groupColumn} will be used for color coding. The other entry, if present, will be coded by line style.} \item{colorValues}{named vector of colors for entries in \code{colorElement}, with names defining unique values in the \code{colorElement} column and values being colors. Also controls the order in which values appear on the plot. If \code{NULL} alphabetical ordering and a default color palette will be used.} \item{title}{string defining the plot title.} \item{subsetRegions}{character vector defining a subset of regions to plot, correspoding to the regions for which the \code{baseline} data was calculated. If \code{NULL} all regions in \code{baseline} are plotted.} \item{sigmaLimits}{numeric vector containing two values defining the \code{c(lower, upper)} bounds of the selection scores to plot.} \item{facetBy}{one of \code{c("region", "group")} specifying which category to facet the plot by, either values in \code{groupColumn} ("group") or regions defined in the \code{regions} slot of the \code{baseline} object ("region"). If this is set to "group", then the region will behave as the \code{groupColumn} for purposes of the \code{colorElement} argument.} \item{style}{type of plot to draw. One of: \itemize{ \item \code{"density"}: plots a set of curves for each probability density function in \code{baseline}, with colors determined by values in the \code{colorElement} column. Faceting is determined by the \code{facetBy} argument. }} \item{sizeElement}{one of \code{c("none", "id", "group")} specifying whether the lines in the plot should be all of the same size (\code{none}) or have their sizes depend on the values in \code{id} or \code{code}.} \item{size}{numeric scaling factor for lines, points and text in the plot.} \item{silent}{if \code{TRUE} do not draw the plot and just return the ggplot2 object; if \code{FALSE} draw the plot.} \item{...}{additional arguments to pass to ggplot2::theme.} } \value{ A ggplot object defining the plot. } \description{ \code{plotBaselineDensity} plots the probability density functions resulting from selection analysis using the BASELINe method. } \examples{ \dontrun{ # Subset example data as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) set.seed(112) db <- dplyr::slice_sample(db, n=100) # Collapse clones db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # Calculate BASELINe baseline <- calcBaseline(db, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc=1) # Grouping the PDFs by the sample and isotype annotations grouped <- groupBaseline(baseline, groupBy=c("sample_id", "c_call")) # Plot density faceted by region with custom isotype colors isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") plotBaselineDensity(grouped, "sample_id", "c_call", colorValues=isotype_colors, colorElement="group", sigmaLimits=c(-1, 1)) # Facet by isotype instead of region sample_colors <- c("-1h"="steelblue", "+7d"="firebrick") plotBaselineDensity(grouped, "sample_id", "c_call", facetBy="group", colorValues=sample_colors, sigmaLimits=c(-1, 1)) } } \seealso{ Takes as input a \link{Baseline} object returned from \link{groupBaseline}. } shazam/man/makeGraphDf.Rd0000644000176200001440000000530314470664105014760 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RegionsExtend.R \name{makeGraphDf} \alias{makeGraphDf} \title{Build a data.frame from a ChangeoClone and an igraph object containing a clonal lineage} \usage{ makeGraphDf( curCloneGraph, curCloneObj, objSeqId = "sequence_id", objSeq = "sequence" ) } \arguments{ \item{curCloneGraph}{an igraph \code{graph} object for the lineage tree generated by \link[alakazam]{buildPhylipLineage}. Note that the field containing the nucleotide sequence in the object must be named \code{sequence}.} \item{curCloneObj}{\link[alakazam]{ChangeoClone} object used to generate the lineage.} \item{objSeqId}{name of the sequence identifier field in \code{curCloneObj}.} \item{objSeq}{name of the nucleotide sequence field in \code{curCloneObj}.} } \value{ A \code{data.frame} with sequence and lineage information, including the the parent nucleotide sequence in the lineage tree(\code{parent_sequence}), an internal parent identifier (\code{parent}), and additional rows for germline sequence and inferred intermediate sequences. Values in the \code{sequence_id} field are renamed to numeric values, prefixed with the clonal grouping identifier and labeled as either \code{"Inferred"} or \code{"Germline"} if they are not an observed sequence. For example, for a lineage with \code{clone_id = 34} the new identifiers would be of the form: \code{"34_Germline"}, \code{"34_Inferred1"}, \code{"34_1"}, \code{"34_2"}, etc. Note that the original sequence identifier is preserved in the \code{orig_sequence_id} field and the original parent sequence identifier is retained in \code{orig_parent}. } \description{ \code{makeGraphDf} creates a data.frame from a \link[alakazam]{ChangeoClone} and an igraph \code{graph} object containing a B cell lineage tree and associated sequence data. The data.frame contains the original fields and additions such as each sequence's parent in the lineage tree, the lineage germline, and additional rows for inferred sequences. } \examples{ # Load and subset example data data(ExampleDb, package = "alakazam") data(ExampleTrees, package = "alakazam") graph <- ExampleTrees[[17]] db <- subset(ExampleDb, clone_id == graph$clone) clone <- alakazam::makeChangeoClone(db) # Extend data with lineage information df <- makeGraphDf(graph, clone) } \seealso{ See \link{observedMutations} to calculate mutation frequencies using \code{parent_sequence} as the reference germline. See \link[alakazam]{ChangeoClone}, \link[alakazam]{buildPhylipLineage}, and \link[igraph]{graph} for details on the input objects. } shazam/man/plotBaselineSummary.Rd0000644000176200001440000001035414470664105016610 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{plotBaselineSummary} \alias{plotBaselineSummary} \title{Plots BASELINe summary statistics} \usage{ plotBaselineSummary( baseline, idColumn, groupColumn = NULL, groupColors = NULL, subsetRegions = NULL, facetBy = c("region", "group"), title = NULL, style = c("summary"), size = 1, silent = FALSE, ... ) } \arguments{ \item{baseline}{either a data.frame returned from \link{summarizeBaseline} or a \code{Baseline} object returned from \link{groupBaseline} containing selection probability density functions and summary statistics.} \item{idColumn}{name of the column in \code{baseline} containing primary identifiers. If the input is a \code{Baseline} object, then this will be a column in the \code{stats} slot of \code{baseline}.} \item{groupColumn}{name of the column in \code{baseline} containing secondary grouping identifiers. If the input is a \code{Baseline} object, then this will be a column in the \code{stats} slot of \code{baseline}.} \item{groupColors}{named vector of colors for entries in \code{groupColumn}, with names defining unique values in the \code{groupColumn} and values being colors. Also controls the order in which groups appear on the plot. If \code{NULL} alphabetical ordering and a default color palette will be used. Has no effect if \code{facetBy="group"}.} \item{subsetRegions}{character vector defining a subset of regions to plot, correspoding to the regions for which the \code{baseline} data was calculated. If \code{NULL} all regions in \code{baseline} are plotted.} \item{facetBy}{one of c("group", "region") specifying which category to facet the plot by, either values in \code{groupColumn} ("group") or regions defined in \code{baseline} ("region"). The data that is not used for faceting will be color coded.} \item{title}{string defining the plot title.} \item{style}{type of plot to draw. One of: \itemize{ \item \code{"summary"}: plots the mean and confidence interval for the selection scores of each value in \code{idColumn}. Faceting and coloring are determine by values in \code{groupColumn} and regions defined in \code{baseline}, depending upon the \code{facetBy} argument. }} \item{size}{numeric scaling factor for lines, points and text in the plot.} \item{silent}{if \code{TRUE} do not draw the plot and just return the ggplot2 object; if \code{FALSE} draw the plot.} \item{...}{additional arguments to pass to ggplot2::theme.} } \value{ A ggplot object defining the plot. } \description{ \code{plotBaselineSummary} plots a summary of the results of selection analysis using the BASELINe method. } \examples{ \donttest{ # Subset example data as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) set.seed(112) db <- dplyr::slice_sample(db, n=25) # Collapse clones db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # Calculate BASELINe baseline <- calcBaseline(db, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc=1) # Grouping the PDFs by sample and isotype annotations grouped <- groupBaseline(baseline, groupBy=c("sample_id", "c_call")) # Plot mean and confidence interval by region with custom group colors isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") plotBaselineSummary(grouped, "sample_id", "c_call", groupColors=isotype_colors, facetBy="region") } } \seealso{ Takes as input either a \link{Baseline} object returned by \link{groupBaseline} or a data.frame returned from \link{summarizeBaseline}. } shazam/man/groupBaseline.Rd0000644000176200001440000000765514470664105015422 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{groupBaseline} \alias{groupBaseline} \title{Group BASELINe PDFs} \usage{ groupBaseline(baseline, groupBy, nproc = 1) } \arguments{ \item{baseline}{\code{Baseline} object containing the \code{db} and the BASELINe posterior probability density functions (PDF) for each of the sequences, as returned by \link{calcBaseline}.} \item{groupBy}{The columns in the \code{db} slot of the \code{Baseline} object by which to group the sequence PDFs.} \item{nproc}{number of cores to distribute the operation over. If \code{nproc} = 0 then the \code{cluster} has already been set and will not be reset.} } \value{ A \link{Baseline} object, containing the modified \code{db} and the BASELINe posterior probability density functions (PDF) for each of the groups. } \description{ \code{groupBaseline} convolves groups of BASELINe posterior probability density functions (PDFs) to get combined PDFs for each group. } \details{ While the selection strengths predicted by BASELINe perform well on average, the estimates for individual sequences can be highly variable, especially when the number of mutations is small. To overcome this, PDFs from sequences grouped by biological or experimental relevance, are convolved to from a single PDF for the selection strength. For example, sequences from each sample may be combined together, allowing you to compare selection across samples. This is accomplished through a fast numerical convolution technique. } \examples{ \dontrun{ # Subset example data from alakazam as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) set.seed(112) db <- dplyr::slice_sample(db, n=200) # Collapse clones db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # Calculate BASELINe baseline <- calcBaseline(db, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc=1) # Group PDFs by sample grouped1 <- groupBaseline(baseline, groupBy="sample_id") sample_colors <- c("-1h"="steelblue", "+7d"="firebrick") plotBaselineDensity(grouped1, idColumn="sample_id", colorValues=sample_colors, sigmaLimits=c(-1, 1)) # Group PDFs by both sample (between variable) and isotype (within variable) grouped2 <- groupBaseline(baseline, groupBy=c("sample_id", "c_call")) isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") plotBaselineDensity(grouped2, idColumn="sample_id", groupColumn="c_call", colorElement="group", colorValues=isotype_colors, sigmaLimits=c(-1, 1)) # Collapse previous isotype (within variable) grouped PDFs into sample PDFs grouped3 <- groupBaseline(grouped2, groupBy="sample_id") sample_colors <- c("-1h"="steelblue", "+7d"="firebrick") plotBaselineDensity(grouped3, idColumn="sample_id", colorValues=sample_colors, sigmaLimits=c(-1, 1)) } } \references{ \enumerate{ \item Yaari G, et al. Quantifying selection in high-throughput immunoglobulin sequencing data sets. Nucleic Acids Res. 2012 40(17):e134. (Corrections at http://selection.med.yale.edu/baseline/correction/) } } \seealso{ To generate the \link{Baseline} object see \link{calcBaseline}. To calculate BASELINe statistics, such as the mean selection strength and the 95\% confidence interval, see \link{summarizeBaseline}. } shazam/man/makeAverage1merSub.Rd0000644000176200001440000000315714470664106016264 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{makeAverage1merSub} \alias{makeAverage1merSub} \title{Make a 1-mer substitution model by averaging over a 5-mer substitution model} \usage{ makeAverage1merSub(sub5mer) } \arguments{ \item{sub5mer}{a 4x1024 matrix such as that returned by \code{createSubstitutionMatrix} and that returned by \code{makeDegenerate5merSub} with \code{extended=FALSE}. Column names should correspond to 5-mers containing the central 1-mer to mutate from. Row names should correspond to nucleotides to mutate into. Nucleotides should include "A", "T", "G", and "C" (case-insensitive).} } \value{ A 4x4 matrix with row names representing nucleotides to mutate from and column names representing nucleotides to mutate into. Rates are normalized by row. } \description{ \code{makeAverage1merSub} averages substitution rates in a 5-mer substitution model to derive a 1-mer substitution model. } \details{ For example, the substitution rate from "A" to "T" in the resultant 1-mer model is derived by averaging the substitution rates into a "T" of all the 5-mers that have an "A" as their central 1-mer. } \examples{ # Make a degenerate 5-mer model (4x1024) based on HKL_S1F (4x4) degenerate5merSub <- makeDegenerate5merSub(sub1mer = HKL_S1F) # Now make a 1-mer model by averaging over the degenerate 5-mer model # Expected to get back HKL_S1F makeAverage1merSub(sub5mer = degenerate5merSub) } \seealso{ See \link{makeDegenerate5merSub} for making a degenerate 5-mer substitution model based on a 1-mer substitution model. } shazam/man/IMGT_SCHEMES.Rd0000644000176200001440000000730014470664105014515 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RegionDefinitions.R \name{IMGT_SCHEMES} \alias{IMGT_SCHEMES} \alias{IMGT_V} \alias{IMGT_V_BY_CODONS} \alias{IMGT_V_BY_REGIONS} \alias{IMGT_V_BY_SEGMENTS} \alias{IMGT_VDJ_BY_REGIONS} \alias{IMGT_VDJ} \title{IMGT unique numbering schemes} \format{ A \link{RegionDefinition} object defining: \itemize{ \item \code{IMGT_V}: The IMGT numbered V segment up to position nucleotide 312. This definition combines the CDR1 and CDR2 into a single CDR region, and FWR1, FWR2 and FWR3 into a single FWR region. CDR3 and FWR4 are excluded as they are downstream of nucleotide 312. \item \code{IMGT_V_BY_CODONS}: The IMGT numbered V segment up to position nucleotide 312. This definition treats each codon, from codon 1 to codon 104, as a distinct region. \item \code{IMGT_V_BY_REGIONS}: The IMGT numbered V segment up to position nucleotide 312. This defines separate regions for each of CDR1, CDR2, FWR1, FWR2 and FWR3. CDR3 and FWR4 are excluded as they are downstream of nucleotide 312. \item \code{IMGT_V_BY_SEGMENTS}: The IMGT numbered V segment up to position nucleotide 312. This definition has no subdivisons and treats the entire V segment as a single region. \item \code{IMGT_VDJ}: IMGT numbered regions for CDR1-3 and FWR1-4 with combined CDR and FWR definitions spanning CDR1-3 and FWR1-4, respectively. Note, unless the definition object has been updated using \link{setRegionBoundaries} this schema will have a value of \code{0} for the \code{seqLength} slot and the \code{boundaries} slot will be empty. This is because these slots depend on the junction length which is unknown in the template scheme. After \link{setRegionBoundaries} has been run, these slots will be populated with the appropriate values for the specied sequence and junction length. \item \code{IMGT_VDJ_BY_REGIONS}: The IMGT numbered regions for FWR1-4 and CDR1-3 with separate region boundaries for each of CDR1, CDR2, CDR3, FWR1, FWR2, FWR3 and FWR4. Note, unless the definition object has been updated using \link{setRegionBoundaries} this schema will have a value of \code{0} for the \code{seqLength} slot and the \code{boundaries} slot will be empty. This is because these slots depend on the junction length which is unknown in the template scheme. After \link{setRegionBoundaries} has been run, these slots will be populated with the appropriate values for the specied sequence and junction length. } } \description{ Sequence region definitions according to the IMGT unique numbering scheme. } \references{ \enumerate{ \item Lefranc MP, et al. IMGT unique numbering for immunoglobulin and T cell receptor variable domains and Ig superfamily V-like domains. Developmental and comparative immunology. 2003 27:55-77. } } shazam/man/TargetingModel-class.Rd0000644000176200001440000000451114470664105016617 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{class} \name{TargetingModel-class} \alias{TargetingModel-class} \alias{TargetingModel} \alias{plot,TargetingModel,missing-method} \alias{TargetingModel-method} \title{S4 class defining a targeting model} \usage{ \S4method{plot}{TargetingModel,missing}(x, y, ...) } \arguments{ \item{x}{\code{TargetingModel} object.} \item{y}{ignored.} \item{...}{arguments to pass to \link{plotMutability}.} } \description{ \code{TargetingModel} defines a common data structure for mutability, substitution and targeting of immunoglobulin (Ig) sequencing data in a 5-mer microsequence context. } \section{Slots}{ \describe{ \item{\code{name}}{Name of the model.} \item{\code{description}}{Description of the model and its source data.} \item{\code{species}}{Genus and species of the source sequencing data.} \item{\code{date}}{Date the model was built.} \item{\code{citation}}{Publication source.} \item{\code{substitution}}{Normalized rates of the center nucleotide of a given 5-mer mutating to a different nucleotide. The substitution model is stored as a 5x3125 matrix of rates. Rows define the mutated nucleotide at the center of each 5-mer, one of \code{c("A", "C", "G", "T", "N")}, and columns define the complete 5-mer of the unmutated nucleotide sequence.} \item{\code{mutability}}{Normalized rates of a given 5-mer being mutated. The mutability model is stored as a numeric vector of length 3125 with mutability rates for each 5-mer. Note that "normalized" means that the mutability rates for the 1024 5-mers that contain no "N" at any position sums up to 1 (as opposed to the entire vector summing up to 1).} \item{\code{targeting}}{Rate matrix of a given mutation ocurring, defined as \eqn{mutability * substitution}. The targeting model is stored as a 5x3125 matrix. Rows define the mutated nucleotide at the center of each 5-mer, one of \code{c("A", "C", "G", "T", "N")}, and columns define the complete 5-mer of the unmutated nucleotide sequence.} \item{\code{numMutS}}{number indicating the number of silent mutations used for estimating mutability.} \item{\code{numMutR}}{number indicating the number of replacement mutations used for estimating mutability.} }} \seealso{ See \link{createTargetingModel} building models from sequencing data. } shazam/man/calcTargetingDistance.Rd0000644000176200001440000000412614470664106017034 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{calcTargetingDistance} \alias{calcTargetingDistance} \title{Calculates a 5-mer distance matrix from a TargetingModel object} \usage{ calcTargetingDistance(model, places = 2) } \arguments{ \item{model}{\link{TargetingModel} object with mutation likelihood information, or a 4x4 1-mer substitution matrix normalized by row with rownames and colnames consisting of "A", "T", "G", and "C".} \item{places}{decimal places to round distances to.} } \value{ For input of \link{TargetingModel}, a matrix of distances for each 5-mer motif with rows names defining the center nucleotide and column names defining the 5-mer nucleotide sequence. For input of 1-mer substitution matrix, a 4x4 symmetric distance matrix. } \description{ \code{calcTargetingDistance} converts either the targeting rates in a \code{TargetingModel} model to a matrix of 5-mer to single-nucleotide mutation distances, or the substitution rates in a 1-mer substitution model to a symmetric distance matrix. } \details{ The targeting model is transformed into a distance matrix by: \enumerate{ \item Converting the likelihood of being mutated \eqn{p=mutability*substitution} to distance \eqn{d=-log10(p)}. \item Dividing this distance by the mean of the distances. \item Converting all infinite, no change (e.g., A->A), and NA distances to zero. } The 1-mer substitution matrix is transformed into a distance matrix by: \enumerate{ \item Symmetrize the 1-mer substitution matrix. \item Converting the rates to distance \eqn{d=-log10(p)}. \item Dividing this distance by the mean of the distances. \item Converting all infinite, no change (e.g., A -> A), and NA distances to zero. } } \examples{ # Calculate targeting distance of HH_S5F dist <- calcTargetingDistance(HH_S5F) # Calculate targeting distance of HH_S1F dist <- calcTargetingDistance(HH_S1F) } \seealso{ See \link{TargetingModel} for this class of objects and \link{createTargetingModel} for building one. } shazam/man/plotSlideWindowTune.Rd0000644000176200001440000001312414470664105016572 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{plotSlideWindowTune} \alias{plotSlideWindowTune} \title{Visualize parameter tuning for sliding window approach} \usage{ plotSlideWindowTune( tuneList, plotFiltered = c("filtered", "remaining", "per_mutation"), percentage = FALSE, jitter.x = FALSE, jitter.x.amt = 0.1, jitter.y = FALSE, jitter.y.amt = 0.1, pchs = 1:length(tuneList), ltys = 1:length(tuneList), cols = 1, plotLegend = TRUE, legendPos = "topright", legendHoriz = FALSE, legendCex = 1, title = NULL, returnRaw = FALSE ) } \arguments{ \item{tuneList}{a list of logical matrices returned by \link{slideWindowTune}.} \item{plotFiltered}{whether to plot the number of filtered ('filtered'), or remaining ('remaining') sequences for each mutation threshold. Use 'per_mutation' to plot the number of sequences at each mutation value. Default is \code{'filtered'}.} \item{percentage}{whether to plot on the y-axis the percentage of filtered sequences (as opposed to the absolute number). Default is \code{FALSE}.} \item{jitter.x}{whether to jitter x-axis values. Default is \code{FALSE}.} \item{jitter.x.amt}{amount of jittering to be applied on x-axis values if \code{jitter.x=TRUE}. Default is 0.1.} \item{jitter.y}{whether to jitter y-axis values. Default is \code{FALSE}.} \item{jitter.y.amt}{amount of jittering to be applied on y-axis values if \code{jitter.y=TRUE}. Default is 0.1.} \item{pchs}{point types to pass on to \link{plot}. Default is \code{1:length(tuneList)}.} \item{ltys}{line types to pass on to \link{plot}. Default is \code{1:length(tuneList)}.} \item{cols}{colors to pass on to \link{plot}.} \item{plotLegend}{whether to plot legend. Default is \code{TRUE}.} \item{legendPos}{position of legend to pass on to \link{legend}. Can be either a numeric vector specifying x-y coordinates, or one of \code{"topright"}, \code{"center"}, etc. Default is \code{"topright"}.} \item{legendHoriz}{whether to make legend horizontal. Default is \code{FALSE}.} \item{legendCex}{numeric values by which legend should be magnified relative to 1.} \item{title}{plot main title. Default is NULL (no title)} \item{returnRaw}{Return a data.frame with sequence counts (TRUE) or a plot. Default is \code{FALSE}.} } \description{ Visualize results from \link{slideWindowTune} } \details{ For each \code{windowSize}, if \code{plotFiltered='filtered'}, the x-axis represents a mutation threshold range, and the y-axis the number of sequences that have at least that number of mutations. If \code{plotFiltered='remaining'}, the y-axis represents the number of sequences that have less mutations than the mutation threshold range. For the same window size, a sequence can be included in the counts for different mutation thresholds. For example, sequence "CCACCAAAA" with germline "AAAAAAAAA" has 4 mutations. This sequence has at least 2 mutations and at least 3 mutations, in a window of size 4. the sequence will be included in the sequence count for mutation thresholds 2 and 3. If \code{plotFiltered='per_mutation'}, the sequences are counted only once for each window size, at their largest mutation threshold. The above example sequence would be included in the sequence count for mutation threshold 3. When plotting, a user-defined \code{amount} of jittering can be applied on values plotted on either axis or both axes via adjusting \code{jitter.x}, \code{jitter.y}, \code{jitter.x.amt} and \code{jitter.y.amt}. This may be help with visually distinguishing lines for different window sizes in case they are very close or identical to each other. If plotting percentages (\code{percentage=TRUE}) and using jittering on the y-axis values (\code{jitter.y=TRUE}), it is strongly recommended that \code{jitter.y.amt} be set very small (e.g. 0.01). \code{NA} for a combination of \code{mutThresh} and \code{windowSize} where \code{mutThresh} is greater than \code{windowSize} will not be plotted. } \examples{ # Use an entry in the example data for input and germline sequence data(ExampleDb, package="alakazam") # Try out thresholds of 2-4 mutations in window sizes of 3-5 nucleotides # on a subset of ExampleDb tuneList <- slideWindowTune(db = ExampleDb[1:10, ], mutThreshRange = 2:4, windowSizeRange = 3:5, verbose = FALSE) # Visualize # Plot numbers of sequences filtered without jittering y-axis values plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered='filtered', jitter.y=FALSE) # Notice that some of the lines overlap # Jittering could help plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered='filtered', jitter.y=TRUE) # Plot numbers of sequences remaining instead of filtered plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered='remaining', jitter.y=TRUE, legendPos="bottomright") # Plot percentages of sequences filtered with a tiny amount of jittering plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered='filtered', percentage=TRUE, jitter.y=TRUE, jitter.y.amt=0.01) } \seealso{ See \link{slideWindowTune} for how to get \code{tuneList}. See \link{jitter} for use of \code{amount} of jittering. } shazam/man/DensityThreshold-class.Rd0000644000176200001440000000223414470664105017206 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/DistToNearest.R \docType{class} \name{DensityThreshold-class} \alias{DensityThreshold-class} \alias{DensityThreshold} \alias{print,DensityThreshold-method} \alias{DensityThreshold-method} \alias{plot,DensityThreshold,missing-method} \title{Output of the \code{dens} method of findThreshold} \usage{ \S4method{print}{DensityThreshold}(x) \S4method{plot}{DensityThreshold,missing}(x, y, ...) } \arguments{ \item{x}{DensityThreshold object} \item{y}{ignored.} \item{...}{arguments to pass to \link{plotDensityThreshold}.} } \description{ \code{DensityThreshold} contains output from the \code{dens} method \link{findThreshold}. } \section{Slots}{ \describe{ \item{\code{x}}{input distance vector with NA or infinite values removed.} \item{\code{bandwidth}}{bandwidth value fit during density estimation.} \item{\code{xdens}}{x-axis (distance value) vector for smoothed density estimate.} \item{\code{ydens}}{y-axis (density) vector for smoothed density estimate.} \item{\code{threshold}}{distance threshold that separates two modes of the input distribution.} }} \seealso{ \link{findThreshold} } shazam/man/plotTune.Rd0000644000176200001440000001144414470664106014425 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{plotTune} \alias{plotTune} \title{Visualize parameter tuning for minNumMutations and minNumSeqMutations} \usage{ plotTune( tuneMtx, thresh, criterion = c("5mer", "3mer", "1mer", "3mer+1mer", "measured", "inferred"), pchs = 1, ltys = 2, cols = 1, plotLegend = TRUE, legendPos = "topright", legendHoriz = FALSE, legendCex = 1 ) } \arguments{ \item{tuneMtx}{a \code{matrix} or a \code{list} of matrices produced by either \link{minNumMutationsTune} or \link{minNumSeqMutationsTune}. In the case of a list, it is assumed that each matrix corresponds to a sample and that all matrices in the list were produced using the same set of trial values of \code{minNumMutations} or \code{minNumSeqMutations}.} \item{thresh}{a number or a vector of indicating the value or the range of values of \code{minNumMutations} or \code{minNumSeqMutations} to plot. Should correspond to the columns of \code{tuneMtx}.} \item{criterion}{one of \code{"5mer"}, \code{"3mer"}, \code{"1mer"}, or \code{"3mer+1mer"} (for \code{tuneMtx} produced by \link{minNumMutationsTune}), or either \code{"measured"} or \code{"inferred"} (for \code{tuneMtx} produced by \link{minNumSeqMutationsTune}).} \item{pchs}{point types to pass on to \link{plot}.} \item{ltys}{line types to pass on to \link{plot}.} \item{cols}{colors to pass on to \link{plot}.} \item{plotLegend}{whether to plot legend. Default is \code{TRUE}. Only applicable if \code{tuneMtx} is a named list with names of the matrices corresponding to the names of the samples.} \item{legendPos}{position of legend to pass on to \link{legend}. Can be either a numeric vector specifying x-y coordinates, or one of \code{"topright"}, \code{"center"}, etc. Default is \code{"topright"}.} \item{legendHoriz}{whether to make legend horizontal. Default is \code{FALSE}.} \item{legendCex}{numeric values by which legend should be magnified relative to 1.} } \description{ Visualize results from \link{minNumMutationsTune} and \link{minNumSeqMutationsTune} } \details{ For \code{tuneMtx} produced by \link{minNumMutationsTune}, for each sample, depending on \code{criterion}, the numbers of 5-mers for which substitution rates are directly computed (\code{"5mer"}), inferred based on inner 3-mers (\code{"3mer"}), inferred based on central 1-mers (\code{"1mer"}), or inferred based on inner 3-mers and central 1-mers (\code{"3mer+1mer"}) are plotted on the y-axis against values of \code{minNumMutations} on the x-axis. For \code{tuneMtx} produced by \link{minNumSeqMutationsTune}, for each sample, depending on \code{criterion}, the numbers of 5-mers for which mutability rates are directly measured (\code{"measured"}) or inferred (\code{"inferred"}) are plotted on the y-axis against values of \code{minNumSeqMutations} on the x-axis. Note that legends will be plotted only if \code{tuneMtx} is a supplied as a named \code{list} of matrices, ideally with names of each \code{matrix} corresponding to those of the samples based on which the matrices were produced, even if \code{plotLegend=TRUE}. } \examples{ \donttest{ # Subset example data to one isotype and 200 sequences data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA") set.seed(112) db <- dplyr::slice_sample(db, n=50) tuneMtx = list() for (i in 1:length(unique(db$sample_id))) { # Get data corresponding to current sample curDb = db[db[["sample_id"]] == unique(db[["sample_id"]])[i], ] # Count the number of mutations per 5-mer subCount = createSubstitutionMatrix(db=curDb, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", multipleMutation="independent", returnModel="5mer", numMutationsOnly=TRUE) # Tune over minNumMutations = 5..50 subTune = minNumMutationsTune(subCount, seq(from=5, to=50, by=5)) tuneMtx = c(tuneMtx, list(subTune)) } # Name tuneMtx after sample names names(tuneMtx) = unique(db[["sample_id"]]) # plot with legend for both samples for a subset of minNumMutations values plotTune(tuneMtx, thresh=c(5, 15, 25, 40), criterion="3mer", pchs=16:17, ltys=1:2, cols=2:3, plotLegend=TRUE, legendPos=c(25, 30)) # plot for only 1 sample for all the minNumMutations values (no legend) plotTune(tuneMtx[[1]], thresh=seq(from=5, to=50, by=5), criterion="3mer") } } \seealso{ See \link{minNumMutationsTune} and \link{minNumSeqMutationsTune} for generating \code{tuneMtx}. } shazam/man/createBaseline.Rd0000644000176200001440000001032214470664105015512 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{createBaseline} \alias{createBaseline} \title{Creates a Baseline object} \usage{ createBaseline( description = "", db = data.frame(), regionDefinition = createRegionDefinition(), testStatistic = "", regions = NULL, numbOfSeqs = matrix(), binomK = matrix(), binomN = matrix(), binomP = matrix(), pdfs = list(), stats = data.frame() ) } \arguments{ \item{description}{\code{character} providing general information regarding the sequences, selection analysis and/or object.} \item{db}{\code{data.frame} containing annotation information about the sequences and selection results.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences.} \item{testStatistic}{\code{character} indicating the statistical framework used to test for selection. For example, \code{"local"} or \code{"focused"} or \code{"imbalanced"}.} \item{regions}{\code{character} vector defining the regions the BASELINe analysis was carried out on. For \code{"cdr"} and \code{"fwr"} or \code{"cdr1"}, \code{"cdr2"}, \code{"cdr3"}, etc. If \code{NULL} then regions will be determined automatically from \code{regionDefinition}.} \item{numbOfSeqs}{\code{matrix} of dimensions \code{r x c} containing the number of sequences or PDFs in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{binomK}{\code{matrix} of dimensions \code{r x c} containing the number of successes in the binomial trials in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{binomN}{\code{matrix} of dimensions \code{r x c} containing the total number of trials in the binomial in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{binomP}{\code{matrix} of dimensions \code{r x c} containing the probability of success in one binomial trial in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{pdfs}{\code{list} of matrices containing PDFs with one item for each defined region (e.g. \code{cdr} and \code{fwr}). Matrices have dimensions \code{r x c} dementions, where:\cr \code{r} = number of rows = number of sequences or groups. \cr \code{c} = number of columns = length of the PDF (default 4001).} \item{stats}{\code{data.frame} of BASELINe statistics, including: mean selection strength (mean Sigma), 95\% confidence intervals, and p-values with positive signs for the presence of positive selection and/or p-values with negative signs for the presence of negative selection.} } \value{ A \code{Baseline} object. } \description{ \code{createBaseline} creates and initialize a \code{Baseline} object. } \details{ Create and initialize a \code{Baseline} object. The \code{testStatistic} indicates the statistical framework used to test for selection. For example, \itemize{ \item \code{local} = CDR_R / (CDR_R + CDR_S). \item \code{focused} = CDR_R / (CDR_R + CDR_S + FWR_S). \item \code{immbalance} = CDR_R + CDR_s / (CDR_R + CDR_S + FWR_S + FWR_R) } For \code{focused} the \code{regionDefinition} must only contain two regions. If more than two regions are defined, then the \code{local} test statistic will be used. For further information on the frame of these tests see Uduman et al. (2011). } \examples{ # Creates an empty Baseline object createBaseline() } \references{ \enumerate{ \item Hershberg U, et al. Improved methods for detecting selection by mutation analysis of Ig V region sequences. Int Immunol. 2008 20(5):683-94. \item Uduman M, et al. Detecting selection in immunoglobulin sequences. Nucleic Acids Res. 2011 39(Web Server issue):W499-504. \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See \link{Baseline} for the return object. } shazam/man/calcObservedMutations.Rd0000644000176200001440000002530314470664105017111 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{calcObservedMutations} \alias{calcObservedMutations} \title{Count the number of observed mutations in a sequence.} \usage{ calcObservedMutations( inputSeq, germlineSeq, regionDefinition = NULL, mutationDefinition = NULL, ambiguousMode = c("eitherOr", "and"), returnRaw = FALSE, frequency = FALSE ) } \arguments{ \item{inputSeq}{input sequence. IUPAC ambiguous characters for DNA are supported.} \item{germlineSeq}{germline sequence. IUPAC ambiguous characters for DNA are supported.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences. Note, only the part of sequences defined in \code{regionDefinition} are analyzed. If NULL, mutations are counted for entire sequence.} \item{mutationDefinition}{\link{MutationDefinition} object defining replacement and silent mutation criteria. If \code{NULL} then replacement and silent are determined by exact amino acid identity.} \item{ambiguousMode}{whether to consider ambiguous characters as \code{"either or"} or \code{"and"} when determining and counting the type(s) of mutations. Applicable only if \code{inputSeq} and/or \code{germlineSeq} contain(s) ambiguous characters. One of \code{c("eitherOr", "and")}. Default is \code{"eitherOr"}.} \item{returnRaw}{return the positions of point mutations and their corresponding mutation types, as opposed to counts of mutations across positions. Also returns the number of bases used as the denominator when calculating frequency. Default is \code{FALSE}.} \item{frequency}{\code{logical} indicating whether or not to calculate mutation frequencies. The denominator used is the number of bases that are not one of "N", "-", or "." in either the input or the germline sequences. If set, this overwrites \code{returnRaw}. Default is \code{FALSE}.} } \value{ For \code{returnRaw=FALSE}, an \code{array} with the numbers of replacement (R) and silent (S) mutations. For \code{returnRaw=TRUE}, a list containing \itemize{ \item \code{$pos}: A data frame whose columns (\code{position}, \code{r}, \code{s}, and \code{region}) indicate, respecitively, the nucleotide position, the number of R mutations at that position, the number of S mutations at that position, and the region in which that nucleotide is in. \item \code{$nonN}: A vector indicating the number of bases in regions defined by \code{regionDefinition} (excluding non-triplet overhang, if any) that are not one of "N", "-", or "." in either the \code{inputSeq} or \code{germlineSeq}. } For \code{frequency=TRUE}, regardless of \code{returnRaw}, an \code{array} with the frequencies of replacement (R) and silent (S) mutations. } \description{ \code{calcObservedMutations} determines all the mutations in a given input sequence compared to its germline sequence. } \details{ \strong{Each mutation is considered independently in the germline context}. For illustration, consider the case where the germline is \code{TGG} and the observed is \code{TAC}. When determining the mutation type at position 2, which sees a change from \code{G} to \code{A}, we compare the codon \code{TGG} (germline) to \code{TAG} (mutation at position 2 independent of other mutations in the germline context). Similarly, when determining the mutation type at position 3, which sees a change from \code{G} to \code{C}, we compare the codon \code{TGG} (germline) to \code{TGC} (mutation at position 3 independent of other mutations in the germline context). If specified, only the part of \code{inputSeq} defined in \code{regionDefinition} is analyzed. For example, when using the default \link{IMGT_V} definition, then mutations in positions beyond 312 will be ignored. Additionally, non-triplet overhang at the sequence end is ignored. Only replacement (R) and silent (S) mutations are included in the results. \strong{Excluded} are: \itemize{ \item Stop mutations E.g.: the case where \code{TAGTGG} is observed for the germline \code{TGGTGG}. \item Mutations occurring in codons where one or both of the observed and the germline involve(s) one or more of "N", "-", or ".". E.g.: the case where \code{TTG} is observed for the germline being any one of \code{TNG}, \code{.TG}, or \code{-TG}. Similarly, the case where any one of \code{TTN}, \code{TT.}, or \code{TT-} is observed for the germline \code{TTG}. } In other words, a result that is \code{NA} or zero indicates absence of R and S mutations, not necessarily all types of mutations, such as the excluded ones mentioned above. \code{NA} is also returned if \code{inputSeq} or \code{germlineSeq} is shorter than 3 nucleotides. } \section{Ambiguous characters}{ When there are ambiguous characters present, the user could choose how mutations involving ambiguous characters are counted through \code{ambiguousMode}. The two available modes are \code{"eitherOr"} and \code{"and"}. \itemize{ \item With \code{"eitherOr"}, ambiguous characters are each expanded but only 1 mutation is recorded. When determining the type of mutation, the priority for different types of mutations, in decreasing order, is as follows: no mutation, replacement mutation, silent mutation, and stop mutation. When counting the number of non-N, non-dash, and non-dot positions, each position is counted only once, regardless of the presence of ambiguous characters. As an example, consider the case where \code{germlineSeq} is \code{"TST"} and \code{inputSeq} is \code{"THT"}. Expanding \code{"H"} at position 2 in \code{inputSeq} into \code{"A"}, \code{"C"}, and \code{"T"}, as well as expanding \code{"S"} at position 2 in \code{germlineSeq} into \code{"C"} and \code{"G"}, one gets: \itemize{ \item \code{"TCT"} (germline) to \code{"TAT"} (observed): replacement \item \code{"TCT"} (germline) to \code{"TCT"} (observed): no mutation \item \code{"TCT"} (germline) to \code{"TTT"} (observed): replacement \item \code{"TGT"} (germline) to \code{"TAT"} (observed): replacement \item \code{"TGT"} (germline) to \code{"TCT"} (observed): replacement \item \code{"TGT"} (germline) to \code{"TTT"} (observed): replacement } Because "no mutation" takes priority over replacement mutation, the final mutation count returned for this example is \code{NA} (recall that only R and S mutations are returned). The number of non-N, non-dash, and non-dot positions is 3. \item With \code{"and"}, ambiguous characters are each expanded and mutation(s) from all expansions are recorded. When counting the number of non-N, non-dash, and non-dot positions, if a position contains ambiguous character(s) in \code{inputSeq} and/or \code{germlineSeq}, the count at that position is taken to be the total number of combinations of germline and observed codons after expansion. Using the same example from above, the final result returned for this example is that there are 5 R mutations at position 2. The number of non-N, non-dash, and non-dot positions is 8, since there are 6 combinations stemming from position 2 after expanding the germline codon (\code{"TST"}) and the observed codon (\code{"THT"}). } } \examples{ # Use an entry in the example data for input and germline sequence data(ExampleDb, package="alakazam") in_seq <- ExampleDb[["sequence_alignment"]][100] germ_seq <- ExampleDb[["germline_alignment_d_mask"]][100] # Identify all mutations in the sequence ex1_raw <- calcObservedMutations(in_seq, germ_seq, returnRaw=TRUE) # Count all mutations in the sequence ex1_count <- calcObservedMutations(in_seq, germ_seq, returnRaw=FALSE) ex1_freq <- calcObservedMutations(in_seq, germ_seq, returnRaw=FALSE, frequency=TRUE) # Compare this with ex1_count table(ex1_raw$pos$region, ex1_raw$pos$r)[, "1"] table(ex1_raw$pos$region, ex1_raw$pos$s)[, "1"] # Compare this with ex1_freq table(ex1_raw$pos$region, ex1_raw$pos$r)[, "1"]/ex1_raw$nonN table(ex1_raw$pos$region, ex1_raw$pos$s)[, "1"]/ex1_raw$nonN # Identify only mutations the V segment minus CDR3 ex2_raw <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, returnRaw=TRUE) # Count only mutations the V segment minus CDR3 ex2_count <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, returnRaw=FALSE) ex2_freq <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, returnRaw=FALSE, frequency=TRUE) # Compare this with ex2_count table(ex2_raw$pos$region, ex2_raw$pos$r)[, "1"] table(ex2_raw$pos$region, ex2_raw$pos$s)[, "1"] # Compare this with ex2_freq table(ex2_raw$pos$region, ex2_raw$pos$r)[, "1"]/ex2_raw$nonN table(ex2_raw$pos$region, ex2_raw$pos$s)[, "1"]/ex2_raw$nonN # Identify mutations by change in hydropathy class ex3_raw <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, mutationDefinition=HYDROPATHY_MUTATIONS, returnRaw=TRUE) # Count mutations by change in hydropathy class ex3_count <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, mutationDefinition=HYDROPATHY_MUTATIONS, returnRaw=FALSE) ex3_freq <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, mutationDefinition=HYDROPATHY_MUTATIONS, returnRaw=FALSE, frequency=TRUE) # Compre this with ex3_count table(ex3_raw$pos$region, ex3_raw$pos$r)[, "1"] table(ex3_raw$pos$region, ex3_raw$pos$s)[, "1"] # Compare this with ex3_freq table(ex3_raw$pos$region, ex3_raw$pos$r)[, "1"]/ex3_raw$nonN table(ex3_raw$pos$region, ex3_raw$pos$s)[, "1"]/ex3_raw$nonN } \seealso{ See \link{observedMutations} for counting the number of observed mutations in a \code{data.frame}. } shazam/man/GmmThreshold-class.Rd0000644000176200001440000000360314470664105016310 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/DistToNearest.R \docType{class} \name{GmmThreshold-class} \alias{GmmThreshold-class} \alias{GmmThreshold} \alias{print,GmmThreshold-method} \alias{GmmThreshold-method} \alias{plot,GmmThreshold,missing-method} \title{Output of the \code{gmm} method of findThreshold} \usage{ \S4method{print}{GmmThreshold}(x) \S4method{plot}{GmmThreshold,missing}(x, y, ...) } \arguments{ \item{x}{GmmThreshold object} \item{y}{ignored.} \item{...}{arguments to pass to \link{plotGmmThreshold}.} } \description{ \code{GmmThreshold} contains output from the \code{gmm} method \link{findThreshold}. It includes parameters of two Gaussian fits and threshold cut. } \section{Slots}{ \describe{ \item{\code{x}}{input distance vector with NA or infinite values removed.} \item{\code{model}}{first-second fit functions.} \item{\code{cutoff}}{type of threshold cut.} \item{\code{a1}}{mixing weight of the first curve.} \item{\code{b1}}{second parameter of the first curve. Either the mean of a Normal distribution or shape of a Gamma distribution.} \item{\code{c1}}{third parameter of the first curve. Either the standard deviation of a Normal distribution or scale of a Gamma distribution.} \item{\code{a2}}{mixing weight of the second curve.} \item{\code{b2}}{second parameter of the second curve. Either the mean of a Normal distribution or shape of a Gamma distribution.} \item{\code{c2}}{third parameter of the second curve. Either the standard deviation of a Normal distribution or scale of a Gamma distribution.} \item{\code{loglk}}{log-likelihood of the fit.} \item{\code{threshold}}{threshold.} \item{\code{sensitivity}}{sensitivity.} \item{\code{specificity}}{specificity.} \item{\code{pvalue}}{p-value from Hartigans' dip statistic (HDS) test. Values less than 0.05 indicate significant bimodality.} }} \seealso{ \link{findThreshold} } shazam/man/distToNearest.Rd0000644000176200001440000002751514470664105015410 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/DistToNearest.R \name{distToNearest} \alias{distToNearest} \title{Distance to nearest neighbor} \usage{ distToNearest( db, sequenceColumn = "junction", vCallColumn = "v_call", jCallColumn = "j_call", model = c("ham", "aa", "hh_s1f", "hh_s5f", "mk_rs1nf", "mk_rs5nf", "m1n_compat", "hs1f_compat"), normalize = c("len", "none"), symmetry = c("avg", "min"), first = TRUE, VJthenLen = TRUE, nproc = 1, fields = NULL, cross = NULL, mst = FALSE, subsample = NULL, progress = FALSE, cellIdColumn = NULL, locusColumn = "locus", locusValues = c("IGH"), onlyHeavy = TRUE, keepVJLgroup = TRUE ) } \arguments{ \item{db}{data.frame containing sequence data.} \item{sequenceColumn}{name of the column containing the junction for grouping and for calculating nearest neighbor distances. Note that while both heavy/long and light/short chain junctions may be used for V-J-length grouping, only the heavy/long chain (IGH, TRB, TRD) junction is used to calculate distances.} \item{vCallColumn}{name of the column containing the V-segment allele calls.} \item{jCallColumn}{name of the column containing the J-segment allele calls.} \item{model}{underlying SHM model, which must be one of \code{c("ham", "aa", "hh_s1f", "hh_s5f", "mk_rs1nf", "hs1f_compat", "m1n_compat")}. See Details for further information.} \item{normalize}{method of normalization. The default is \code{"len"}, which divides the distance by the length of the sequence group. If \code{"none"} then no normalization if performed.} \item{symmetry}{if model is hs5f, distance between seq1 and seq2 is either the average (avg) of seq1->seq2 and seq2->seq1 or the minimum (min).} \item{first}{if \code{TRUE} only the first call of the gene assignments is used. if \code{FALSE} the union of ambiguous gene assignments is used to group all sequences with any overlapping gene calls.} \item{VJthenLen}{logical value specifying whether to perform partitioning as a 2-stage process. If \code{TRUE}, partitions are made first based on V and J gene, and then further split based on junction lengths corresponding to \code{sequenceColumn}. If \code{FALSE}, perform partition as a 1-stage process during which V gene, J gene, and junction length are used to create partitions simultaneously. Defaults to \code{TRUE}.} \item{nproc}{number of cores to distribute the function over.} \item{fields}{additional fields to use for grouping.} \item{cross}{character vector of column names to use for grouping to calculate distances across groups. Meaning the columns that define self versus others.} \item{mst}{if \code{TRUE}, return comma-separated branch lengths from minimum spanning tree.} \item{subsample}{number of sequences to subsample for speeding up pairwise-distance-matrix calculation. Subsampling is performed without replacement in each V-J-length group of heavy chain sequences. If \code{subsample} is larger than the unique number of heavy chain sequences in each VJL group, then the subsampling process is ignored for that group. For each heavy chain sequence in \code{db}, the reported \code{dist_nearest} is the distance to the closest heavy chain sequence in the subsampled set for the V-J-length group. If \code{NULL} no subsampling is performed.} \item{progress}{if \code{TRUE} print a progress bar.} \item{cellIdColumn}{name of the character column containing cell identifiers or barcodes. If specified, grouping will be performed in single-cell mode with the behavior governed by the \code{locusColumn} and \code{onlyHeavy} arguments. If set to \code{NULL} then the bulk sequencing data is assumed.} \item{locusColumn}{name of the column containing locus information. Valid loci values are "IGH", "IGI", "IGK", "IGL", "TRA", "TRB", "TRD", and "TRG".} \item{locusValues}{Loci values to focus the analysis on.} \item{onlyHeavy}{use only the IGH (BCR) or TRB/TRD (TCR) sequences for grouping. Only applicable to single-cell data. Ignored if \code{cellIdColumn=NULL}. See \link[alakazam]{groupGenes} for further details.} \item{keepVJLgroup}{logical value specifying whether to keep in the output the the column column indicating grouping based on V-J-length combinations. Only applicable for 1-stage partitioning (i.e. \code{VJthenLen=FALSE}). Also see \link[alakazam]{groupGenes}.} } \value{ Returns a modified \code{db} data.frame with nearest neighbor distances between heavy chain sequences in the \code{dist_nearest} column if \code{cross=NULL}. If \code{cross} was specified, distances will be added as the \code{cross_dist_nearest} column. Note that distances between light/short (IGK, IGL, TRA, TRG) chain sequences are not calculated, even if light/short chains were used for V-J-length grouping via \code{onlyHeavy=FALSE}. Light/short chain sequences, if any, will have \code{NA} in the \code{dist_nearest} output column. Note that the output \code{vCallColumn} and \code{jCallColumn} columns will be converted to type \code{character} if they were type \code{factor} in the input \code{db}. } \description{ Get non-zero distance of every heavy chain (\code{IGH}) sequence (as defined by \code{sequenceColumn}) to its nearest sequence in a partition of heavy chains sharing the same V gene, J gene, and junction length (V-J-length), or in a partition of single cells with heavy/long chains sharing the same heavy/long chain V-J-length combination, or of single cells with heavy/long and light/short chains sharing the same heavy/long chain V-J-length and light/short chain V-J-length combinations. } \details{ To invoke single-cell mode the \code{cellIdColumn} argument must be specified and \code{locusColumn} must be correct. Otherwise, \code{distToNearest} will be run with bulk sequencing assumptions, using all input sequences regardless of the values in the \code{locusColumn} column. Under single-cell mode, only heavy/long chain (IGH, TRB, TRD) sequences will be used for calculating nearest neighbor distances. Under non-single-cell mode, all input sequences will be used for calculating nearest neighbor distances, regardless of the values in the \code{locusColumn} field (if present). Values in the \code{locusColumn} must be one of \code{c("IGH", "IGI", "IGK", "IGL")} for BCR or \code{c("TRA", "TRB", "TRD", "TRG")} for TCR sequences. Otherwise, the function returns an error message and stops. For single-cell mode, the input format is the same as that for \link[alakazam]{groupGenes}. Namely, each row represents a sequence/chain. Sequences/chains from the same cell are linked by a cell ID in the \code{cellIdColumn} field. In this mode, there is a choice of whether grouping should be done by (a) using IGH (BCR) or TRB/TRD (TCR) sequences only or (b) using IGH plus IGK/IGL (BCR) or TRB/TRD plus TRA/TRG (TCR). This is governed by the \code{onlyHeavy} argument. Note, \code{distToNearest} required that each cell (each unique value in \code{cellIdColumn}) correspond to only a single \code{IGH} (BCR) or \code{TRB/TRD} (TCR) sequence. The distance to nearest neighbor can be used to estimate a threshold for assigning Ig sequences to clonal groups. A histogram of the resulting vector is often bimodal, with the ideal threshold being a value that separates the two modes. The following distance measures are accepted by the \code{model} parameter. \itemize{ \item \code{"ham"}: Single nucleotide Hamming distance matrix from \link[alakazam]{getDNAMatrix} with gaps assigned zero distance. \item \code{"aa"}: Single amino acid Hamming distance matrix from \link[alakazam]{getAAMatrix}. \item \code{"hh_s1f"}: Human single nucleotide distance matrix derived from \link{HH_S1F} with \link{calcTargetingDistance}. \item \code{"hh_s5f"}: Human 5-mer nucleotide context distance matix derived from \link{HH_S5F} with \link{calcTargetingDistance}. \item \code{"mk_rs1nf"}: Mouse single nucleotide distance matrix derived from \link{MK_RS1NF} with \link{calcTargetingDistance}. \item \code{"mk_rs5nf"}: Mouse 5-mer nucleotide context distance matrix derived from \link{MK_RS1NF} with \link{calcTargetingDistance}. \item \code{"hs1f_compat"}: Backwards compatible human single nucleotide distance matrix used in SHazaM v0.1.4 and Change-O v0.3.3. \item \code{"m1n_compat"}: Backwards compatibley mouse single nucleotide distance matrix used in SHazaM v0.1.4 and Change-O v0.3.3. } Note on \code{NA}s: if, for a given combination of V gene, J gene, and junction length, there is only 1 heavy chain sequence (as defined by \code{sequenceColumn}), \code{NA} is returned instead of a distance (since it has no heavy/long chain neighbor). If for a given combination there are multiple heavy/long chain sequences but only 1 unique one, (in which case every heavy/long cahin sequence in this group is the de facto nearest neighbor to each other, thus giving rise to distances of 0), \code{NA}s are returned instead of zero-distances. Note on \code{subsample}: Subsampling is performed independently in each V-J-length group for heavy/long chain sequences. If \code{subsample} is larger than number of heavy/long chain sequences in the group, it is ignored. In other words, subsampling is performed only on groups in which the number of heavy/long chain sequences is equal to or greater than \code{subsample}. \code{dist_nearest} has values calculated using all heavy chain sequences in the group for groups with fewer than \code{subsample} heavy/long chain sequences, and values calculated using a subset of heavy/long chain sequences for the larger groups. To select a value of \code{subsample}, it can be useful to explore the group sizes in \code{db} (and the number of heavy/long chain sequences in those groups). } \examples{ # Subset example data to one sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, sample_id == "-1h") # Use genotyped V assignments, Hamming distance, and normalize by junction length # First partition based on V and J assignments, then by junction length # Take into consideration ambiguous V and J annotations dist <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, VJthenLen=TRUE, normalize="len") # Plot histogram of non-NA distances p1 <- ggplot(data=subset(dist, !is.na(dist_nearest))) + theme_bw() + ggtitle("Distance to nearest: Hamming") + xlab("distance") + geom_histogram(aes(x=dist_nearest), binwidth=0.025, fill="steelblue", color="white") plot(p1) } \references{ \enumerate{ \item Smith DS, et al. Di- and trinucleotide target preferences of somatic mutagenesis in normal and autoreactive B cells. J Immunol. 1996 156:2642-52. \item Glanville J, Kuo TC, von Budingen H-C, et al. Naive antibody gene-segment frequencies are heritable and unaltered by chronic lymphocyte ablation. Proc Natl Acad Sci USA. 2011 108(50):20066-71. \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4:358. } } \seealso{ See \link{calcTargetingDistance} for generating nucleotide distance matrices from a \link{TargetingModel} object. See \link{HH_S5F}, \link{HH_S1F}, \link{MK_RS1NF}, \link[alakazam]{getDNAMatrix}, and \link[alakazam]{getAAMatrix} for individual model details. \link[alakazam]{getLocus} to get locus values based on allele calls. } shazam/man/calcExpectedMutations.Rd0000644000176200001440000000650614470664105017105 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{calcExpectedMutations} \alias{calcExpectedMutations} \title{Calculate expected mutation frequencies of a sequence} \usage{ calcExpectedMutations( germlineSeq, inputSeq = NULL, targetingModel = HH_S5F, regionDefinition = NULL, mutationDefinition = NULL ) } \arguments{ \item{germlineSeq}{germline (reference) sequence.} \item{inputSeq}{input (observed) sequence. If this is not \code{NULL}, then \code{germlineSeq} will be processed to be the same same length as \code{inputSeq} and positions in \code{germlineSeq} corresponding to positions with Ns in \code{inputSeq} will also be assigned an N.} \item{targetingModel}{\link{TargetingModel} object. Default is \link{HH_S5F}.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences.} \item{mutationDefinition}{\link{MutationDefinition} object defining replacement and silent mutation criteria. If \code{NULL} then replacement and silent are determined by exact amino acid identity.} } \value{ A \code{numeric} vector of the expected frequencies of mutations in the regions in the \code{regionDefinition}. For example, when using the default \link{IMGT_V} definition, which defines positions for CDR and FWR, the following columns are calculated: \itemize{ \item \code{mu_expected_cdr_r}: number of replacement mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_expected_cdr_s}: number of silent mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_expected_fwr_r}: number of replacement mutations in FWR1, FWR2 and FWR3 of the V-segment. \item \code{mu_expected_fwr_s}: number of silent mutations in FWR1, FWR2 and FWR3 of the V-segment. } } \description{ \code{calcExpectedMutations} calculates the expected mutation frequencies of a given sequence. This is primarily a helper function for \link{expectedMutations}. } \details{ \code{calcExpectedMutations} calculates the expected mutation frequencies of a given sequence and its germline. Note, only the part of the sequences defined in \code{regionDefinition} are analyzed. For example, when using the default \link{IMGT_V} definition, mutations in positions beyond 312 will be ignored. } \examples{ # Load example data data(ExampleDb, package="alakazam") # Use first entry in the exampled data for input and germline sequence in_seq <- ExampleDb[["sequence_alignment"]][1] germ_seq <- ExampleDb[["germline_alignment_d_mask"]][1] # Identify all mutations in the sequence calcExpectedMutations(germ_seq,in_seq) # Identify only mutations the V segment minus CDR3 calcExpectedMutations(germ_seq, in_seq, regionDefinition=IMGT_V) # Define mutations based on hydropathy calcExpectedMutations(germ_seq, in_seq, regionDefinition=IMGT_V, mutationDefinition=HYDROPATHY_MUTATIONS) } \seealso{ \link{expectedMutations} calls this function. To create a custom \code{targetingModel} see \link{createTargetingModel}. See \link{calcObservedMutations} for getting observed mutation counts. } shazam/man/MK_RS5NF.Rd0000644000176200001440000000225014470664105014031 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{MK_RS5NF} \alias{MK_RS5NF} \title{Mouse kappa light chain, replacement and silent, 5-mer, non-functional targeting model.} \format{ \link{TargetingModel} object. } \usage{ MK_RS5NF } \description{ 5-mer model of somatic hypermutation targeting based on analysis of replacement and silent mutations in non-functional kappa light chain Ig sequences from NP-immunized Mus musculus. } \references{ \enumerate{ \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of Immunology, 197(9), 3566-3574. } } \seealso{ See \link{MK_RS1NF} for the 1-mer substitution matrix from the same publication; \link{HH_S5F} for the human heavy chain silent 5-mer functional targeting model; \link{HKL_S5F} for the human light chain silent 5-mer functional targeting model; and \link{U5N} for the uniform 5-mer null targeting model. } \keyword{datasets} shazam/man/plotDensityThreshold.Rd0000644000176200001440000000466514470664105017014 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/DistToNearest.R \name{plotDensityThreshold} \alias{plotDensityThreshold} \title{Plot findThreshold results for the density method} \usage{ plotDensityThreshold( data, cross = NULL, xmin = NULL, xmax = NULL, breaks = NULL, binwidth = NULL, title = NULL, size = 1, silent = FALSE, ... ) } \arguments{ \item{data}{\link{DensityThreshold} object output by the \code{"density"} method of \link{findThreshold}.} \item{cross}{numeric vector of distances from \link{distToNearest} to draw as a histogram below the \code{data} histogram for comparison purposes.} \item{xmin}{minimum limit for plotting the x-axis. If \code{NULL} the limit will be set automatically.} \item{xmax}{maximum limit for plotting the x-axis. If \code{NULL} the limit will be set automatically.} \item{breaks}{number of breaks to show on the x-axis. If \code{NULL} the breaks will be set automatically.} \item{binwidth}{binwidth for the histogram. If \code{NULL} the binwidth will be set automatically to the bandwidth parameter determined by \link{findThreshold}.} \item{title}{string defining the plot title.} \item{size}{numeric value for the plot line sizes.} \item{silent}{if \code{TRUE} do not draw the plot and just return the ggplot2 object; if \code{FALSE} draw the plot.} \item{...}{additional arguments to pass to ggplot2::theme.} } \value{ A ggplot object defining the plot. } \description{ \code{plotDensityThreshold} plots the results from \code{"density"} method of \link{findThreshold}, including the smoothed density estimate, input nearest neighbor distance histogram, and threshold selected. } \examples{ \donttest{ # Subset example data to one sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, sample_id == "-1h") # Use nucleotide Hamming distance and normalize by junction length db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # To find the threshold cut, call findThreshold function for "gmm" method. output <- findThreshold(db$dist_nearest, method="density") print(output) # Plot plotDensityThreshold(output) } } \seealso{ See \link{DensityThreshold} for the the input object definition and \link{findThreshold} for generating the input object. See \link{distToNearest} calculating nearest neighbor distances. } shazam/man/extendSubstitutionMatrix.Rd0000644000176200001440000000254714470664106017730 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{extendSubstitutionMatrix} \alias{extendSubstitutionMatrix} \title{Extends a substitution model to include Ns.} \usage{ extendSubstitutionMatrix(substitutionModel) } \arguments{ \item{substitutionModel}{matrix of 5-mers substitution counts built by \link{createSubstitutionMatrix}.} } \value{ A 5x3125 matrix of normalized substitution rate for each 5-mer motif with rows names defining the center nucleotide, one of \code{c("A", "C", "G", "T", "N")}, and column names defining the 5-mer nucleotide sequence. } \description{ \code{extendSubstitutionMatrix} extends a 5-mer nucleotide substitution model with 5-mers that include Ns by averaging over all corresponding 5-mers without Ns. } \examples{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") # Create model using only silent mutations sub_model <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call",model="s") ext_model <- extendSubstitutionMatrix(sub_model) } \seealso{ \link{createSubstitutionMatrix}, \link{extendMutabilityMatrix} } shazam/man/slideWindowDb.Rd0000644000176200001440000000413614470664105015350 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{slideWindowDb} \alias{slideWindowDb} \title{Sliding window approach towards filtering sequences in a \code{data.frame}} \usage{ slideWindowDb( db, sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", mutThresh = 6, windowSize = 10, nproc = 1 ) } \arguments{ \item{db}{\code{data.frame} containing sequence data.} \item{sequenceColumn}{name of the column containing IMGT-gapped sample sequences.} \item{germlineColumn}{name of the column containing IMGT-gapped germline sequences.} \item{mutThresh}{threshold on the number of mutations in \code{windowSize} consecutive nucleotides. Must be between 1 and \code{windowSize} inclusive.} \item{windowSize}{length of consecutive nucleotides. Must be at least 2.} \item{nproc}{Number of cores to distribute the operation over. If the \code{cluster} has already been set earlier, then pass the \code{cluster}. This will ensure that it is not reset.} } \value{ a logical vector. The length of the vector matches the number of input sequences in \code{db}. Each entry in the vector indicates whether the corresponding input sequence should be filtered based on the given parameters. } \description{ \code{slideWindowDb} determines whether each input sequence in a \code{data.frame} contains equal to or more than a given number of mutations in a given length of consecutive nucleotides (a "window") when compared to their respective germline sequence. } \examples{ # Use an entry in the example data for input and germline sequence data(ExampleDb, package="alakazam") # Apply the sliding window approach on a subset of ExampleDb slideWindowDb(db=ExampleDb[1:10, ], sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", mutThresh=6, windowSize=10, nproc=1) } \seealso{ See \link{slideWindowSeq} for applying the sliding window approach on a single sequence. See \link{slideWindowTune} for parameter tuning for \code{mutThresh} and \code{windowSize}. } shazam/man/TargetingMatrix-class.Rd0000644000176200001440000000122714470664105017024 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{class} \name{TargetingMatrix-class} \alias{TargetingMatrix-class} \alias{TargetingMatrix} \title{S4 class defining a targeting matrix} \description{ \code{TargetingMatrix} defines a data structure for just the targeting matrix (as opposed to the entire \code{TargetingModel}) } \section{Slots}{ \describe{ \item{\code{.Data}}{matrix.} \item{\code{numMutS}}{number indicating the number of silent mutations used for estimating mutability.} \item{\code{numMutR}}{number indicating the number of replacement mutations used for estimating mutability.} }} shazam/man/U5N.Rd0000644000176200001440000000106114470664105013213 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{U5N} \alias{U5N} \title{Uniform 5-mer null targeting model.} \format{ A \link{TargetingModel} object. } \usage{ U5N } \description{ A null 5-mer model of somatic hypermutation targeting where all substitution, mutability and targeting rates are uniformly distributed. } \seealso{ See \link{HH_S5F} and \link{HKL_S5F} for the human 5-mer targeting models; and \link{MK_RS5NF} for the mouse 5-mer targeting model. } \keyword{datasets} shazam/man/shazam-package.Rd0000644000176200001440000000317514506074767015501 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Shazam.R \docType{package} \name{shazam-package} \alias{shazam-package} \alias{_PACKAGE} \title{shazam: Immunoglobulin Somatic Hypermutation Analysis} \description{ Provides a computational framework for analyzing mutations in immunoglobulin (Ig) sequences. Includes methods for Bayesian estimation of antigen-driven selection pressure, mutational load quantification, building of somatic hypermutation (SHM) models, and model-dependent distance calculations. Also includes empirically derived models of SHM for both mice and humans. Citations: Gupta and Vander Heiden, et al (2015) \doi{10.1093/bioinformatics/btv359}, Yaari, et al (2012) \doi{10.1093/nar/gks457}, Yaari, et al (2013) \doi{10.3389/fimmu.2013.00358}, Cui, et al (2016) \doi{10.4049/jimmunol.1502263}. } \seealso{ Useful links: \itemize{ \item \url{http://shazam.readthedocs.io} \item Report bugs at \url{https://bitbucket.org/kleinstein/shazam/issues} } } \author{ \strong{Maintainer}: Susanna Marquez \email{susanna.marquez@yale.edu} Authors: \itemize{ \item Mohamed Uduman \email{mohamed.uduman@yale.edu} \item Namita Gupta \email{namita.gupta@yale.edu} \item Julian Zhou \email{julian.zhou@yale.edu} \item Nima Nouri \email{nima.nouri@yale.edu} \item Noah Yann Lee \email{noah.yann.lee@yale.edu} \item Jason Vander Heiden \email{jason.vanderheiden@gmail.com} \item Gur Yaari \email{gur.yaari@biu.ac.il} \item Steven Kleinstein \email{steven.kleinstein@yale.edu} [copyright holder] } Other contributors: \itemize{ \item Ang Cui \email{angcui@mit.edu} [contributor] } } \keyword{internal} shazam/man/makeDegenerate5merSub.Rd0000644000176200001440000000406214470664106016755 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{makeDegenerate5merSub} \alias{makeDegenerate5merSub} \title{Make a degenerate 5-mer substitution model based on a 1-mer substitution model} \usage{ makeDegenerate5merSub(sub1mer, extended = FALSE) } \arguments{ \item{sub1mer}{a 4x4 matrix containing (normalized) substitution rates. Row names should correspond to nucleotides to mutate from. Column names should correspond to nucleotides to mutate into. Nucleotides should include "A", "T", "G", and "C" (case-insensitive).} \item{extended}{whether to return the unextended (\code{extended=FALSE}) or extended (\code{extended=TRUE}) 5-mer substitution model. Default is \code{FALSE}.} } \value{ For \code{extended=FALSE}, a 4x1024 matrix. For \code{extended=TRUE}, a 5x3125 matrix. } \description{ \code{makeDegenerate5merSub} populates substitution rates from a 1-mer substitution model into 5-mers with corresponding central 1-mers. } \details{ As a concrete example, consider a 1-mer substitution model in which substitution rates from "A" to "T", "G", and "C" are, respectively, 0.1, 0.6, and 0.3. In the resultant degenerate 5-mer substitution model, all the 5-mers (columns) that have an "A" as their central 1-mer would have substitution rates (rows) of 0.1, 0.6, and 0.3 to "T", "G", and "C" respectively. When \code{extended=TRUE}, \code{extendSubstitutionMatrix} is called to extend the 4x1024 substitution matrix. } \examples{ # Make a degenerate 5-mer model (4x1024) based on HKL_S1F (4x4) # Note: not to be confused with HKL_S5F@substitution, which is non-degenerate degenerate5merSub <- makeDegenerate5merSub(sub1mer = HKL_S1F) # Look at a few 5-mers degenerate5merSub[, c("AAAAT", "AACAT", "AAGAT", "AATAT")] } \seealso{ See \link{makeAverage1merSub} for making a 1-mer substitution model by taking the average of a 5-mer substitution model. See \link{extendSubstitutionMatrix} for extending the substitution matrix. } shazam/man/observedMutations.Rd0000644000176200001440000001703714470664105016333 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{observedMutations} \alias{observedMutations} \title{Calculate observed numbers of mutations} \usage{ observedMutations( db, sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", regionDefinition = NULL, mutationDefinition = NULL, ambiguousMode = c("eitherOr", "and"), frequency = FALSE, combine = FALSE, nproc = 1, cloneColumn = "clone_id", juncLengthColumn = "junction_length" ) } \arguments{ \item{db}{\code{data.frame} containing sequence data.} \item{sequenceColumn}{\code{character} name of the column containing input sequences. IUPAC ambiguous characters for DNA are supported.} \item{germlineColumn}{\code{character} name of the column containing the germline or reference sequence. IUPAC ambiguous characters for DNA are supported.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences. If NULL, mutations are counted for entire sequence. To use regions definitions, sequences in \code{sequenceColum} and \code{germlineColumn} must be aligned, following the IMGT schema.} \item{mutationDefinition}{\link{MutationDefinition} object defining replacement and silent mutation criteria. If \code{NULL} then replacement and silent are determined by exact amino acid identity.} \item{ambiguousMode}{whether to consider ambiguous characters as \code{"either or"} or \code{"and"} when determining and counting the type(s) of mutations. Applicable only if \code{sequenceColumn} and/or \code{germlineColumn} contain(s) ambiguous characters. One of \code{c("eitherOr", "and")}. Default is \code{"eitherOr"}.} \item{frequency}{\code{logical} indicating whether or not to calculate mutation frequencies. Default is \code{FALSE}.} \item{combine}{\code{logical} indicating whether for each sequence should the mutation counts for the different regions (CDR, FWR) and mutation types be combined and return one value of count/frequency per sequence instead of multiple values. Default is \code{FALSE}.} \item{nproc}{number of cores to distribute the operation over. If the cluster has already been set the call function with \code{nproc} = 0 to not reset or reinitialize. Default is \code{nproc} = 1.} \item{cloneColumn}{clone id column name in \code{db}} \item{juncLengthColumn}{junction length column name in \code{db}} } \value{ A modified \code{db} \code{data.frame} with observed mutation counts for each sequence listed. The columns names are dynamically created based on the regions in the \code{regionDefinition}. For example, when using the \link{IMGT_V} definition, which defines positions for CDR and FWR, the following columns are added: \itemize{ \item \code{mu_count_cdr_r}: number of replacement mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_count_cdr_s}: number of silent mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_count_fwr_r}: number of replacement mutations in FWR1, FWR2 and FWR3 of the V-segment. \item \code{mu_count_fwr_s}: number of silent mutations in FWR1, FWR2 and FWR3 of the V-segment. } If \code{frequency=TRUE}, R and S mutation frequencies are calculated over the number of non-N positions in the specified regions. \itemize{ \item \code{mu_freq_cdr_r}: frequency of replacement mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_freq_cdr_s}: frequency of silent mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_freq_fwr_r}: frequency of replacement mutations in FWR1, FWR2 and FWR3 of the V-segment. \item \code{mu_freq_fwr_s}: frequency of silent mutations in FWR1, FWR2 and FWR3 of the V-segment. } If \code{frequency=TRUE} and \code{combine=TRUE}, the mutations and non-N positions are aggregated and a single \code{mu_freq} value is returned \itemize{ \item \code{mu_freq}: frequency of replacement and silent mutations in the specified region } } \description{ \code{observedMutations} calculates the observed number of mutations for each sequence in the input \code{data.frame}. } \details{ Mutation counts are determined by comparing a reference sequence to the input sequences in the column specified by \code{sequenceColumn}. See \link{calcObservedMutations} for more technical details, \strong{including criteria for which sequence differences are included in the mutation counts and which are not}. The mutations are binned as either replacement (R) or silent (S) across the different regions of the sequences as defined by \code{regionDefinition}. Typically, this would be the framework (FWR) and complementarity determining (CDR) regions of IMGT-gapped nucleotide sequences. Mutation counts are appended to the input \code{db} as additional columns. If \code{db} includes lineage information, such as the \code{parent_sequence} column created by \link{makeGraphDf}, the reference sequence can be set to use that field as reference sequence using the \code{germlineColumn} argument. } \examples{ # Subset example data data(ExampleDb, package="alakazam") db <- ExampleDb[1:10, ] # Calculate mutation frequency over the entire sequence db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", frequency=TRUE, nproc=1) # Count of V-region mutations split by FWR and CDR # With mutations only considered replacement if charge changes db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, mutationDefinition=CHARGE_MUTATIONS, nproc=1) # Count of VDJ-region mutations, split by FWR and CDR db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_VDJ, nproc=1) # Extend data with lineage information data(ExampleTrees, package="alakazam") graph <- ExampleTrees[[17]] clone <- alakazam::makeChangeoClone(subset(ExampleDb, clone_id == graph$clone)) gdf <- makeGraphDf(graph, clone) # Count of mutations between observed sequence and immediate ancenstor db_obs <- observedMutations(gdf, sequenceColumn="sequence", germlineColumn="parent_sequence", regionDefinition=IMGT_VDJ, nproc=1) } \seealso{ \link{calcObservedMutations} is called by this function to get the number of mutations in each sequence grouped by the \link{RegionDefinition}. See \link{IMGT_SCHEMES} for a set of predefined \link{RegionDefinition} objects. See \link{expectedMutations} for calculating expected mutation frequencies. See \link{makeGraphDf} for creating the field \code{parent_sequence}. } shazam/man/setRegionBoundaries.Rd0000644000176200001440000000572414470664105016571 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RegionsExtend.R \name{setRegionBoundaries} \alias{setRegionBoundaries} \title{Build a RegionDefinition object that includes CDR3 and FWR4.} \usage{ setRegionBoundaries(juncLength, sequenceImgt, regionDefinition = NULL) } \arguments{ \item{juncLength}{junction length of the sequence.} \item{sequenceImgt}{IMGT-numbered sequence.} \item{regionDefinition}{\code{RegionDefinition} type to calculate the region definition for. Can be one of \code{IMGT_VDJ_BY_REGIONS} or \code{IMGT_VDJ}, which are template definitions that include CDR1-3 and FWR1-4. Only these two regions include all CDR1-3 and FWR1-4 regions. If this argument is set to \code{NULL}, then an empty \code{RegionDefinition} will be returned.} } \value{ A \code{RegionDefinition} object that includes CDR1-3 and FWR1-4 for the \code{sequenceImgt}, \code{juncLength}, and \code{regionDefinition} specified. For \code{regionDefinition=IMGT_VDJ_BY_REGIONS}, the returned \code{RegionDefinition} includes: \itemize{ \item \code{fwr1}: Positions 1 to 78. \item \code{cdr1}: Positions 79 to 114. \item \code{fwr2}: Positions 115 to 165. \item \code{cdr2}: Positions 166 to 195. \item \code{fwr3}: Positions 196 to 312. \item \code{cdr3}: Positions 313 to (313 + juncLength - 6) since the junction sequence includes (on the left) the last codon from FWR3 and (on the right) the first codon from FWR4. \item \code{fwr4}: Positions (313 + juncLength - 6 + 1) to the end of the sequence. } For \code{regionDefinition=IMGT_VDJ}, the returned \code{RegionDefinition} includes: \itemize{ \item \code{fwr}: Positions belonging to a FWR. \item \code{cdr}: Positions belonging to a CDR. } In the case that the \code{regionDefinition} argument is not one of the extended regions (\code{IMGT_VDJ_BY_REGIONS} or \code{IMGT_VDJ}), the input \code{regionDefinition} is returned as is. } \description{ \code{setRegionBoundaries} takes as input a junction length and an IMGT-numbered sequence and outputs a custom \code{RegionDefinition} object that includes the boundary definitions of CDR1-3 and FWR1-4 for that sequence. In contrast to the universal \code{RegionDefinition} object that end with FWR3, the returned definition is per-sequence due to variable junction lengths. } \examples{ # Load and subset example data data(ExampleDb, package = "alakazam") len <- ExampleDb$junction_length[1] sequence <- ExampleDb$sequence_alignment[1] region <- setRegionBoundaries(len, sequence, regionDefinition = IMGT_VDJ) } \seealso{ See \link{RegionDefinition} for the return object. See \link{IMGT_SCHEMES} for a set of predefined \code{RegionDefinition} objects. } shazam/man/HH_S5F.Rd0000644000176200001440000000175614470664105013573 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{HH_S5F} \alias{HH_S5F} \title{Human heavy chain, silent, 5-mer, functional targeting model.} \format{ A \link{TargetingModel} object. } \usage{ HH_S5F } \description{ 5-mer model of somatic hypermutation targeting based on analysis of silent mutations in functional heavy chain Ig sequences from Homo sapiens. } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See \link{HH_S1F} for the 1-mer substitution matrix from the same publication; \link{HKL_S5F} for the human light chain 5-mer targeting model; \link{MK_RS5NF} for the mouse 5-mer targeting model; and \link{U5N} for the uniform 5-mer null targeting model. } \keyword{datasets} shazam/man/MutationDefinition-class.Rd0000644000176200001440000000214114470664105017520 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationDefinitions.R \docType{class} \name{MutationDefinition-class} \alias{MutationDefinition-class} \alias{MutationDefinition} \title{S4 class defining replacement and silent mutation definitions} \description{ \code{MutationDefinition} defines a common data structure for defining the whether a mutation is annotated as a replacement or silent mutation. } \section{Slots}{ \describe{ \item{\code{name}}{name of the MutationDefinition.} \item{\code{description}}{description of the model and its source.} \item{\code{classes}}{named character vectors with single-letter amino acid codes as names and amino acid classes as values, with \code{NA} assigned to set of characters \code{c("X", "*", "-", ".")}. Replacement (R) is be defined as a change in amino acid class and silent (S) as no change in class.} \item{\code{codonTable}}{matrix of codons (columns) and substitutions (rows).} \item{\code{citation}}{publication source.} }} \seealso{ See \link{MUTATION_SCHEMES} for a set of predefined \code{MutationDefinition} objects. } shazam/man/MUTATION_SCHEMES.Rd0000644000176200001440000000230414470664105015214 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationDefinitions.R \name{MUTATION_SCHEMES} \alias{MUTATION_SCHEMES} \alias{CHARGE_MUTATIONS} \alias{HYDROPATHY_MUTATIONS} \alias{POLARITY_MUTATIONS} \alias{VOLUME_MUTATIONS} \title{Amino acid mutation definitions} \format{ A \link{MutationDefinition} object defining: \itemize{ \item \code{CHARGE_MUTATIONS}: Amino acid mutations are defined by changes in side chain charge class. \item \code{HYDROPATHY_MUTATIONS}: Amino acid mutations are defined by changes in side chain hydrophobicity class. \item \code{POLARITY_MUTATIONS}: Amino acid mutations are defined by changes in side chain polarity class. \item \code{VOLUME_MUTATIONS}: Amino acid mutations are defined by changes in side chain volume class. } } \description{ Definitions of replacement (R) and silent (S) mutations for different amino acid physicochemical classes. } \references{ \enumerate{ \item \url{https://www.imgt.org/IMGTeducation/Aide-memoire/_UK/aminoacids/IMGTclasses.html} } } shazam/man/expectedMutations.Rd0000644000176200001440000001012414470664105016311 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{expectedMutations} \alias{expectedMutations} \title{Calculate expected mutation frequencies} \usage{ expectedMutations( db, sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment", targetingModel = HH_S5F, regionDefinition = NULL, mutationDefinition = NULL, nproc = 1, cloneColumn = "clone_id", juncLengthColumn = "junction_length" ) } \arguments{ \item{db}{\code{data.frame} containing sequence data.} \item{sequenceColumn}{\code{character} name of the column containing input sequences.} \item{germlineColumn}{\code{character} name of the column containing the germline or reference sequence.} \item{targetingModel}{\link{TargetingModel} object. Default is \link{HH_S5F}.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences. To use regions definitions, sequences in \code{sequenceColum} and \code{germlineColumn} must be aligned, following the IMGT schema.} \item{mutationDefinition}{\link{MutationDefinition} object defining replacement and silent mutation criteria. If \code{NULL} then replacement and silent are determined by exact amino acid identity.} \item{nproc}{\code{numeric} number of cores to distribute the operation over. If the cluster has already been set the call function with \code{nproc} = 0 to not reset or reinitialize. Default is \code{nproc} = 1.} \item{cloneColumn}{clone id column name in \code{db}} \item{juncLengthColumn}{junction length column name in \code{db}} } \value{ A modified \code{db} \code{data.frame} with expected mutation frequencies for each region defined in \code{regionDefinition}. The columns names are dynamically created based on the regions in \code{regionDefinition}. For example, when using the \link{IMGT_V} definition, which defines positions for CDR and FWR, the following columns are added: \itemize{ \item \code{mu_expected_cdr_r}: number of replacement mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_expected_cdr_s}: number of silent mutations in CDR1 and CDR2 of the V-segment. \item \code{mu_expected_fwr_r}: number of replacement mutations in FWR1, FWR2 and FWR3 of the V-segment. \item \code{mu_expected_fwr_s}: number of silent mutations in FWR1, FWR2 and FWR3 of the V-segment. } } \description{ \code{expectedMutations} calculates the expected mutation frequencies for each sequence in the input \code{data.frame}. } \details{ Only the part of the sequences defined in \code{regionDefinition} are analyzed. For example, when using the \link{IMGT_V} definition, mutations in positions beyond 312 will be ignored. } \examples{ # Subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHA", "IGHG") & sample_id == "+7d") set.seed(112) db <- dplyr::slice_sample(db, n=100) # Calculate expected mutations over V region db_exp <- expectedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, nproc=1) # Calculate hydropathy expected mutations over V region db_exp <- expectedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, mutationDefinition=HYDROPATHY_MUTATIONS, nproc=1) } \seealso{ \link{calcExpectedMutations} is called by this function to calculate the expected mutation frequencies. See \link{observedMutations} for getting observed mutation counts. See \link{IMGT_SCHEMES} for a set of predefined \link{RegionDefinition} objects. } shazam/man/testBaseline.Rd0000644000176200001440000000544414470664105015237 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{testBaseline} \alias{testBaseline} \title{Two-sided test of BASELINe PDFs} \usage{ testBaseline(baseline, groupBy) } \arguments{ \item{baseline}{\code{Baseline} object containing the \code{db} and grouped BASELINe PDFs returned by \link{groupBaseline}.} \item{groupBy}{string defining the column in the \code{db} slot of the \code{Baseline} containing sequence or group identifiers.} } \value{ A data.frame with test results containing the following columns: \itemize{ \item \code{region}: sequence region, such as \code{cdr} and \code{fwr}. \item \code{test}: string defining the groups be compared. The string is formated as the conclusion associated with the p-value in the form \code{GROUP1 != GROUP2}. Meaning, the p-value for rejection of the null hypothesis that GROUP1 and GROUP2 have equivalent distributions. \item \code{pvalue}: two-sided p-value for the comparison. \item \code{fdr}: FDR corrected \code{pvalue}. } } \description{ \code{testBaseline} performs a two-sample signifance test of BASELINe posterior probability density functions (PDFs). } \examples{ \donttest{ # Subset example data as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) set.seed(112) db <- dplyr::slice_sample(db, n=200) # Collapse clones db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # Calculate BASELINe baseline <- calcBaseline(db, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc=1) # Group PDFs by the isotype grouped <- groupBaseline(baseline, groupBy="c_call") # Visualize isotype PDFs plot(grouped, "c_call") # Perform test on isotype PDFs testBaseline(grouped, groupBy="c_call") } } \references{ \enumerate{ \item Yaari G, et al. Quantifying selection in high-throughput immunoglobulin sequencing data sets. Nucleic Acids Res. 2012 40(17):e134. (Corretions at http://selection.med.yale.edu/baseline/correction/) } } \seealso{ To generate the \link{Baseline} input object see \link{groupBaseline}. } shazam/man/slideWindowTune.Rd0000644000176200001440000000765314470664105015745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{slideWindowTune} \alias{slideWindowTune} \title{Parameter tuning for sliding window approach} \usage{ slideWindowTune( db, sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", dbMutList = NULL, mutThreshRange, windowSizeRange, verbose = TRUE, nproc = 1 ) } \arguments{ \item{db}{\code{data.frame} containing sequence data.} \item{sequenceColumn}{name of the column containing IMGT-gapped sample sequences.} \item{germlineColumn}{name of the column containing IMGT-gapped germline sequences.} \item{dbMutList}{if supplied, this should be a list consisting of \code{data.frame}s returned as \code{$pos} in the nested list produced by \link{calcObservedMutations} with \code{returnRaw=TRUE}; otherwise, \link{calcObservedMutations} is called on columns \code{sequenceColumn} and \code{germlineColumn} of \code{db}. Default is \code{NULL}.} \item{mutThreshRange}{range of threshold on the number of mutations in \code{windowSize} consecutive nucleotides to try. Must be between 1 and maximum \code{windowSizeRange} inclusive.} \item{windowSizeRange}{range of length of consecutive nucleotides to try. The lower end must be at least 2.} \item{verbose}{whether to print out messages indicating current progress. Default is \code{TRUE}.} \item{nproc}{Number of cores to distribute the operation over. If the \code{cluster} has already been set earlier, then pass the \code{cluster}. This will ensure that it is not reset.} } \value{ a list of logical matrices. Each matrix corresponds to a \code{windowSize} in \code{windowSizeRange}. Each column in a matrix corresponds to a \code{mutThresh} in \code{mutThreshRange}. Each row corresponds to a sequence. \code{TRUE} values mean the sequences has at least the number of mutations specified in the column name, for that \code{windowSize}. } \description{ Apply \link{slideWindowDb} over a search grid made of combinations of \code{mutThresh} and \code{windowSize} to help with picking a pair of values for these parameters. Parameter tuning can be performed by choosing a combination that gives a reasonable number of filtered/remaining sequences. } \details{ If, in a given combination of \code{mutThresh} and \code{windowSize}, \code{mutThresh} is greater than \code{windowSize}, \code{NA}s will be returned for that particular combination. A message indicating that the combination has been "skipped" will be printed if \code{verbose=TRUE}. If \link{calcObservedMutations} was previously run on \code{db} and saved, supplying \code{$pos} from the saved result as \code{dbMutList} could save time by skipping a second call of \link{calcObservedMutations}. This could be helpful especially when \code{db} is large. } \examples{ # Load and subset example data data(ExampleDb, package="alakazam") db <- ExampleDb[1:5, ] # Try out thresholds of 2-4 mutations in window sizes of 7-9 nucleotides. # In this case, all combinations are legal. slideWindowTune(db, mutThreshRange=2:4, windowSizeRange=7:9) # Illegal combinations are skipped, returning NAs. slideWindowTune(db, mutThreshRange=2:4, windowSizeRange=2:4, verbose=FALSE) # Run calcObservedMutations separately exDbMutList <- sapply(1:5, function(i) { calcObservedMutations(inputSeq=db[["sequence_alignment"]][i], germlineSeq=db[["germline_alignment_d_mask"]][i], returnRaw=TRUE)$pos }) slideWindowTune(db, dbMutList=exDbMutList, mutThreshRange=2:4, windowSizeRange=2:4) } \seealso{ \link{slideWindowDb} is called on \code{db} for tuning. See \link{slideWindowTunePlot} for visualization. See \link{calcObservedMutations} for generating \code{dbMutList}. } shazam/man/createTargetingModel.Rd0000644000176200001440000000773714470664106016716 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{createTargetingModel} \alias{createTargetingModel} \title{Creates a TargetingModel} \usage{ createTargetingModel( db, model = c("s", "rs"), sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", vCallColumn = "v_call", multipleMutation = c("independent", "ignore"), minNumMutations = 50, minNumSeqMutations = 500, modelName = "", modelDescription = "", modelSpecies = "", modelCitation = "", modelDate = NULL ) } \arguments{ \item{db}{data.frame containing sequence data.} \item{model}{type of model to create. The default model, "s", builds a model by counting only silent mutations. \code{model="s"} should be used for data that includes functional sequences. Setting \code{model="rs"} creates a model by counting both replacement and silent mutations and may be used on fully non-functional sequence data sets.} \item{sequenceColumn}{name of the column containing IMGT-gapped sample sequences.} \item{germlineColumn}{name of the column containing IMGT-gapped germline sequences.} \item{vCallColumn}{name of the column containing the V-segment allele calls.} \item{multipleMutation}{string specifying how to handle multiple mutations occuring within the same 5-mer. If \code{"independent"} then multiple mutations within the same 5-mer are counted indepedently. If \code{"ignore"} then 5-mers with multiple mutations are excluded from the otal mutation tally.} \item{minNumMutations}{minimum number of mutations required to compute the 5-mer substitution rates. If the number of mutations for a 5-mer is below this threshold, its substitution rates will be estimated from neighboring 5-mers. Default is 50.} \item{minNumSeqMutations}{minimum number of mutations in sequences containing each 5-mer to compute the mutability rates. If the number is smaller than this threshold, the mutability for the 5-mer will be inferred. Default is 500.} \item{modelName}{name of the model.} \item{modelDescription}{description of the model and its source data.} \item{modelSpecies}{genus and species of the source sequencing data.} \item{modelCitation}{publication source.} \item{modelDate}{date the model was built. If \code{NULL} the current date will be used.} } \value{ A \link{TargetingModel} object. } \description{ \code{createTargetingModel} creates a 5-mer \code{TargetingModel}. } \details{ \strong{Caution: The targeting model functions do NOT support ambiguous characters in their inputs. You MUST make sure that your input and germline sequences do NOT contain ambiguous characters (especially if they are clonal consensuses returned from \code{collapseClones}).} } \examples{ \donttest{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:80,] # Create model using only silent mutations and ignore multiple mutations model <- createTargetingModel(db, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", multipleMutation="ignore") # View top 5 mutability estimates head(sort(model@mutability, decreasing=TRUE), 5) # View number of silent mutations used for estimating mutability model@numMutS } } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See \link{TargetingModel} for the return object. See \link{plotMutability} plotting a mutability model. See \link{createSubstitutionMatrix}, \link{extendSubstitutionMatrix}, \link{createMutabilityMatrix}, \link{extendMutabilityMatrix} and \link{createTargetingMatrix} for component steps in building a model. } shazam/man/HKL_S1F.Rd0000644000176200001440000000211414470664105013673 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{HKL_S1F} \alias{HKL_S1F} \title{Human kappa and lambda chain, silent, 1-mer, functional substitution model.} \format{ A 4x4 matrix of nucleotide substitution rates. The rates are normalized, therefore each row sums up to 1. } \usage{ HKL_S1F } \description{ 1-mer substitution model of somatic hypermutation based on analysis of silent mutations in functional kappa and lambda light chain Ig sequences from Homo sapiens. } \note{ Reported in Table III in Cui et al, 2016. } \references{ \enumerate{ \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of Immunology, 197(9), 3566-3574. } } \seealso{ See \link{HH_S1F} for the human heavy chain 1-mer substitution model and \link{MK_RS1NF} for the mouse light chain 1-mer substitution model. } \keyword{datasets} shazam/man/RegionDefinition-class.Rd0000644000176200001440000000207514470664105017151 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RegionDefinitions.R \docType{class} \name{RegionDefinition-class} \alias{RegionDefinition-class} \alias{RegionDefinition} \title{S4 class defining a region definition} \description{ \code{RegionDefinition} defines a common data structure for defining the region boundaries of an Ig sequence. } \section{Slots}{ \describe{ \item{\code{name}}{name of the RegionDefinition.} \item{\code{description}}{description of the model and its source.} \item{\code{boundaries}}{\code{factor} defining the region boundaries of the sequence. The levels and values of \code{boundaries} determine the number of regions.} \item{\code{seqLength}}{length of the sequence.} \item{\code{regions}}{levels of the boundaries; e.g, \code{c("cdr", "fwr")}.} \item{\code{labels}}{labels for the boundary and mutations combinations; e.g., \code{c("cdr_r", "cdr_s", "fwr_r", "fwr_s")}.} \item{\code{citation}}{publication source.} }} \seealso{ See \link{IMGT_SCHEMES} for a set of predefined \code{RegionDefinition} objects. } shazam/man/makeAverage1merMut.Rd0000644000176200001440000000276214470664106016301 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{makeAverage1merMut} \alias{makeAverage1merMut} \title{Make a 1-mer mutability model by averaging over a 5-mer mutability model} \usage{ makeAverage1merMut(mut5mer) } \arguments{ \item{mut5mer}{a named vector of length 1024 such as that returned by \code{createMutabilityMatrix} and that returned by \code{makeDegenerate5merMut} with \code{extended=FALSE}. Names should correspond to 5-mers made up of "A", "T", "G", and "C" (case-insensitive). \code{NA} values are allowed.} } \value{ A named vector of length 4 containing normalized mutability rates. } \description{ \code{makeAverage1merMut} averages mutability rates in a 5-mer mutability model to derive a 1-mer mutability model. } \details{ For example, the mutability rate of "A" in the resultant 1-mer model is derived by averaging the mutability rates of all the 5-mers that have an "A" as their central 1-mer, followed by normalization. } \examples{ # Make a degenerate 5-mer model (length of 1024) based on a 1-mer model example1merMut <- c(A=0.2, T=0.1, C=0.4, G=0.3) degenerate5merMut <- makeDegenerate5merMut(mut1mer = example1merMut) # Now make a 1-mer model by averaging over the degenerate 5-mer model # Expected to get back example1merMut makeAverage1merMut(mut5mer = degenerate5merMut) } \seealso{ See \link{makeDegenerate5merMut} for making a degenerate 5-mer mutability model based on a 1-mer mutability model. } shazam/man/extendMutabilityMatrix.Rd0000644000176200001440000000410614470664106017330 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{extendMutabilityMatrix} \alias{extendMutabilityMatrix} \title{Extends a mutability model to include Ns.} \usage{ extendMutabilityMatrix(mutabilityModel) } \arguments{ \item{mutabilityModel}{vector of 5-mer mutability rates built by \link{createMutabilityMatrix}.} } \value{ A \code{MutabilityModel} containing a 3125 vector of normalized mutability rates for each 5-mer motif with names defining the 5-mer nucleotide sequence. Note that "normalized" means that the mutability rates for the 1024 5-mers that contain no "N" at any position sums up to 1 (as opposed to the entire vector summing up to 1). If the input \code{mutabilityModel} is of class \code{MutabilityModel}, then the output \code{MutabilityModel} will carry over the input \code{numMutS} and \code{numMutR} slots. } \description{ \code{extendMutabilityMatrix} extends a 5-mer nucleotide mutability model with 5-mers that include Ns by averaging over all corresponding 5-mers without Ns. } \examples{ \donttest{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") set.seed(112) db <- dplyr::slice_sample(db, n=75) # Create model using only silent mutations and ignore multiple mutations sub_model <- createSubstitutionMatrix(db, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") mut_model <- createMutabilityMatrix(db, sub_model, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") ext_model <- extendMutabilityMatrix(mut_model) } } \seealso{ \link{createMutabilityMatrix}, \link{extendSubstitutionMatrix}, \link{MutabilityModel} } shazam/man/collapseClones.Rd0000644000176200001440000005154014470664105015561 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{collapseClones} \alias{collapseClones} \title{Constructs effective clonal sequences for all clones} \usage{ collapseClones( db, cloneColumn = "clone_id", sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", muFreqColumn = NULL, regionDefinition = NULL, method = c("mostCommon", "thresholdedFreq", "catchAll", "mostMutated", "leastMutated"), minimumFrequency = NULL, includeAmbiguous = FALSE, breakTiesStochastic = FALSE, breakTiesByColumns = NULL, expandedDb = FALSE, nproc = 1, juncLengthColumn = "junction_length", fields = NULL ) } \arguments{ \item{db}{\code{data.frame} containing sequence data. Required.} \item{cloneColumn}{\code{character} name of the column containing clonal identifiers. Required.} \item{sequenceColumn}{\code{character} name of the column containing input sequences. Required. The length of each input sequence should match that of its corresponding germline sequence.} \item{germlineColumn}{\code{character} name of the column containing germline sequences. Required. The length of each germline sequence should match that of its corresponding input sequence.} \item{muFreqColumn}{\code{character} name of the column containing mutation frequency. Optional. Applicable to the \code{"mostMutated"} and \code{"leastMutated"} methods. If not supplied, mutation frequency is computed by calling \code{observedMutations}. Default is \code{NULL}. See Cautions for note on usage.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences. Optional. Default is \code{NULL}.} \item{method}{method for calculating input consensus sequence. Required. One of \code{"thresholdedFreq"}, \code{"mostCommon"}, \code{"catchAll"}, \code{"mostMutated"}, or \code{"leastMutated"}. See "Methods" for details.} \item{minimumFrequency}{frequency threshold for calculating input consensus sequence. Applicable to and required for the \code{"thresholdedFreq"} method. A canonical choice is 0.6. Default is \code{NULL}.} \item{includeAmbiguous}{whether to use ambiguous characters to represent positions at which there are multiple characters with frequencies that are at least \code{minimumFrequency} or that are maximal (i.e. ties). Applicable to and required for the \code{"thresholdedFreq"} and \code{"mostCommon"} methods. Default is \code{FALSE}. See "Choosing ambiguous characters" for rules on choosing ambiguous characters.} \item{breakTiesStochastic}{In case of ties, whether to randomly pick a sequence from sequences that fulfill the criteria as consensus. Applicable to and required for all methods except for \code{"catchAll"}. Default is \code{FALSE}. See "Methods" for details.} \item{breakTiesByColumns}{A list of the form \code{list(c(col_1, col_2, ...), c(fun_1, fun_2, ...))}, where \code{col_i} is a \code{character} name of a column in \code{db}, and \code{fun_i} is a function to be applied on that column. Currently, only \code{max} and \code{min} are supported. Note that the two \code{c()}'s in \code{list()} are essential (i.e. if there is only 1 column, the list should be of the form \code{list(c(col_1), c(func_1))}. Applicable to and optional for the \code{"mostMutated"} and \code{"leastMutated"} methods. If supplied, \code{fun_i}'s are applied on \code{col_i}'s to help break ties. Default is \code{NULL}. See "Methods" for details.} \item{expandedDb}{\code{logical} indicating whether or not to return the expanded \code{db}, containing all the sequences (as opposed to returning just one sequence per clone).} \item{nproc}{Number of cores to distribute the operation over. If the \code{cluster} has already been set earlier, then pass the \code{cluster}. This will ensure that it is not reset.} \item{juncLengthColumn}{\code{character} name of the column containing the junction length. Needed when \code{regionDefinition} includes CDR3 and FWR4.} \item{fields}{additional fields used for grouping. Use sample_id, to avoid combining sequences with the same clone_id that belong to different sample_id.} } \value{ A modified \code{db} with the following additional columns: \itemize{ \item \code{clonal_sequence}: effective sequence for the clone. \item \code{clonal_germline}: germline sequence for the clone. \item \code{clonal_sequence_mufreq}: mutation frequency of \code{clonal_sequence}; only added for the \code{"mostMutated"} and \code{"leastMutated"} methods. } \code{clonal_sequence} is generated with the method of choice indicated by \code{method}, and \code{clonal_germline} is generated with the \code{"mostCommon"} method, along with, where applicable, user-defined parameters such as \code{minimumFrequency}, \code{includeAmbiguous}, \code{breakTiesStochastic}, and \code{breakTiesByColumns}. } \description{ \code{collapseClones} creates effective input and germline sequences for each clonal group and appends columns containing the consensus sequences to the input \code{data.frame}. } \section{Consensus lengths}{ For each clone, \code{clonal_sequence} and \code{clonal_germline} have the same length. \itemize{ \item For the \code{"thresholdedFreq"}, \code{"mostCommon"}, and \code{"catchAll"} methods: The length of the consensus sequences is determined by the longest possible consensus sequence (baesd on \code{inputSeq} and \code{germlineSeq}) and \code{regionDefinition@seqLength} (if supplied), whichever is shorter. Given a set of sequences of potentially varying lengths, the longest possible length of their consensus sequence is taken to be the longest length along which there is information contained at every nucleotide position across majority of the sequences. Majority is defined to be greater than \code{floor(n/2)}, where \code{n} is the number of sequences. If the longest possible consensus length is 0, there will be a warning and an empty string (\code{""}) will be returned. If a length limit is defined by supplying a \code{regionDefinition} via \code{regionDefinition@seqLength}, the consensus length will be further restricted to the shorter of the longest possible length and \code{regionDefinition@seqLength}. \item For the \code{"mostMutated"} and \code{"leastMutated"} methods: The length of the consensus sequences depends on that of the most/least mutated input sequence, and, if supplied, the length limit defined by \code{regionDefinition@seqLength}, whichever is shorter. If the germline consensus computed using the \code{"mostCommon"} method is longer than the most/least mutated input sequence, the germline consensus is trimmed to be of the same length as the input consensus. } } \section{Methods}{ The descriptions below use "sequences" as a generalization of input sequences and germline sequences. \itemize{ \item \code{method="thresholdedFreq"} A threshold must be supplied to the argument \code{minimumFrequency}. At each position along the length of the consensus sequence, the frequency of each nucleotide/character across sequences is tabulated. The nucleotide/character whose frequency is at least (i.e. \code{>=}) \code{minimumFrequency} becomes the consensus; if there is none, the consensus nucleotide will be \code{"N"}. When there are ties (frequencies of multiple nucleotides/characters are at least \code{minimumFrequency}), this method can be deterministic or stochastic, depending on additional parameters. \itemize{ \item With \code{includeAmbiguous=TRUE}, ties are resolved deterministically by representing ties using ambiguous characters. See "Choosing ambiguous characters" for how ambiguous characters are chosen. \item With \code{breakTiesStochastic=TRUE}, ties are resolved stochastically by randomly picking a character amongst the ties. \item When both \code{TRUE}, \code{includeAmbiguous} takes precedence over \code{breakTiesStochastic}. \item When both \code{FALSE}, the first character from the ties is taken to be the consensus following the order of \code{"A"}, \code{"T"}, \code{"G"}, \code{"C"}, \code{"N"}, \code{"."}, and \code{"-"}. } Below are some examples looking at a single position based on 5 sequences with \code{minimumFrequency=0.6}, \code{includeAmbiguous=FALSE}, and \code{breakTiesStochastic=FALSE}: \itemize{ \item If the sequences have \code{"A"}, \code{"A"}, \code{"A"}, \code{"T"}, \code{"C"}, the consensus will be \code{"A"}, because \code{"A"} has frequency 0.6, which is at least \code{minimumFrequency}. \item If the sequences have \code{"A"}, \code{"A"}, \code{"T"}, \code{"T"}, \code{"C"}, the consensus will be \code{"N"}, because none of \code{"A"}, \code{"T"}, or \code{"C"} has frequency that is at least \code{minimumFrequency}. } \item \code{method="mostCommon"} The most frequent nucleotide/character across sequences at each position along the length of the consensus sequence makes up the consensus. When there are ties (multiple nucleotides/characters with equally maximal frequencies), this method can be deterministic or stochastic, depending on additional parameters. The same rules for breaking ties for \code{method="thresholdedFreq"} apply. Below are some examples looking at a single position based on 5 sequences with \code{includeAmbiguous=FALSE}, and \code{breakTiesStochastic=FALSE}: \itemize{ \item If the sequences have \code{"A"}, \code{"A"}, \code{"T"}, \code{"A"}, \code{"C"}, the consensus will be \code{"A"}. \item If the sequences have \code{"T"}, \code{"T"}, \code{"C"}, \code{"C"}, \code{"G"}, the consensus will be \code{"T"}, because \code{"T"} is before \code{"C"} in the order of \code{"A"}, \code{"T"}, \code{"G"}, \code{"C"}, \code{"N"}, \code{"."}, and \code{"-"}. } \item \code{method="catchAll"} This method returns a consensus sequence capturing most of the information contained in the sequences. Ambiguous characters are used where applicable. See "Choosing ambiguous characters" for how ambiguous characters are chosen. This method is deterministic and does not involve breaking ties. Below are some examples for \code{method="catchAll"} looking at a single position based on 5 sequences: \itemize{ \item If the sequences have \code{"N"}, \code{"N"}, \code{"N"}, \code{"N"}, \code{"N"}, the consensus will be \code{"N"}. \item If the sequences have \code{"N"}, \code{"A"}, \code{"A"}, \code{"A"}, \code{"A"}, the consensus will be \code{"A"}. \item If the sequences have \code{"N"}, \code{"A"}, \code{"G"}, \code{"A"}, \code{"A"}, the consensus will be \code{"R"}. \item If the sequences have \code{"-"}, \code{"-"}, \code{"."}, \code{"."}, \code{"."}, the consensus will be \code{"-"}. \item If the sequences have \code{"-"}, \code{"-"}, \code{"-"}, \code{"-"}, \code{"-"}, the consensus will be \code{"-"}. \item If the sequences have \code{"."}, \code{"."}, \code{"."}, \code{"."}, \code{"."}, the consensus will be \code{"."}. } \item \code{method="mostMutated"} and \code{method="leastMutated"} These methods return the most/least mutated sequence as the consensus sequence. When there are ties (multple sequences have the maximal/minimal mutation frequency), this method can be deterministic or stochastic, depending on additional parameters. \itemize{ \item With \code{breakTiesStochastic=TRUE}, ties are resolved stochastically by randomly picking a sequence out of sequences with the maximal/minimal mutation frequency. \item When \code{breakTiesByColumns} is supplied, ties are resolved deterministically. Column by column, a function is applied on the column and sequences with column value matching the functional value are retained, until ties are resolved or columns run out. In the latter case, the first remaining sequence is taken as the consensus. \item When \code{breakTiesStochastic=TRUE} and \code{breakTiesByColumns} is also supplied, \code{breakTiesStochastic} takes precedence over \code{breakTiesByColumns}. \item When \code{breakTiesStochastic=FALSE} and \code{breakTiesByColumns} is not supplied (i.e. \code{NULL}), the sequence that appears first amongst the ties is taken as the consensus. } } } \section{Choosing ambiguous characters}{ Ambiguous characters may be present in the returned consensuses when using the \code{"catchAll"} method and when using the \code{"thresholdedFreq"} or \code{"mostCommon"} methods with \code{includeAmbiguous=TRUE}. The rules on choosing ambiguous characters are as follows: \itemize{ \item If a position contains only \code{"N"} across sequences, the consensus at that position is \code{"N"}. \item If a position contains one or more of \code{"A"}, \code{"T"}, \code{"G"}, or \code{"C"}, the consensus will be an IUPAC character representing all of the characters present, regardless of whether \code{"N"}, \code{"-"}, or \code{"."} is present. \item If a position contains only \code{"-"} and \code{"."} across sequences, the consensus at that position is taken to be \code{"-"}. \item If a position contains only one of \code{"-"} or \code{"."} across sequences, the consensus at that position is taken to be the character present. } } \section{Cautions}{ \itemize{ \item Note that this function does not perform multiple sequence alignment. As a prerequisite, it is assumed that the sequences in \code{sequenceColumn} and \code{germlineColumn} have been aligned somehow. In the case of immunoglobulin repertoire analysis, this usually means that the sequences are IMGT-gapped. \item When using the \code{"mostMutated"} and \code{"leastMutated"} methods, if you supply both \code{muFreqColumn} and \code{regionDefinition}, it is your responsibility to ensure that the mutation frequency in \code{muFreqColumn} was calculated with sequence lengths restricted to the \strong{same} \code{regionDefinition} you are supplying. Otherwise, the "most/least mutated" sequence you obtain might not be the most/least mutated given the \code{regionDefinition} supplied, because your mutation frequency was based on a \code{regionDefinition} different from the one supplied. \item If you intend to run \code{collapseClones} before building a 5-mer targeting model, you \strong{must} choose parameters such that your collapsed clonal consensuses do \strong{not} include ambiguous characters. This is because the targeting model functions do NOT support ambiguous characters in their inputs. } } \examples{ # Subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHA", "IGHG") & sample_id == "+7d" & clone_id \%in\% c("3100", "3141", "3184")) # thresholdedFreq method, resolving ties deterministically without using ambiguous characters clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # mostCommon method, resolving ties deterministically using ambiguous characters clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="mostCommon", includeAmbiguous=TRUE, breakTiesStochastic=FALSE) # Make a copy of db that has a mutation frequency column db2 <- observedMutations(db, frequency=TRUE, combine=TRUE) # mostMutated method, resolving ties stochastically clones <- collapseClones(db2, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="mostMutated", muFreqColumn="mu_freq", breakTiesStochastic=TRUE, breakTiesByColumns=NULL) # mostMutated method, resolving ties deterministically using additional columns clones <- collapseClones(db2, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="mostMutated", muFreqColumn="mu_freq", breakTiesStochastic=FALSE, breakTiesByColumns=list(c("duplicate_count"), c(max))) # Build consensus for V segment only # Capture all nucleotide variations using ambiguous characters clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="catchAll", regionDefinition=IMGT_V) # Return the same number of rows as the input clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="mostCommon", expandedDb=TRUE) } \seealso{ See \link{IMGT_SCHEMES} for a set of predefined \link{RegionDefinition} objects. } shazam/man/slideWindowSeq.Rd0000644000176200001440000000351214470664105015550 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{slideWindowSeq} \alias{slideWindowSeq} \title{Sliding window approach towards filtering a single sequence} \usage{ slideWindowSeq(inputSeq, germlineSeq, mutThresh, windowSize) } \arguments{ \item{inputSeq}{input sequence.} \item{germlineSeq}{germline sequence.} \item{mutThresh}{threshold on the number of mutations in \code{windowSize} consecutive nucleotides. Must be between 1 and \code{windowSize} inclusive.} \item{windowSize}{length of consecutive nucleotides. Must be at least 2.} } \value{ \code{TRUE} if there are equal to or more than \code{mutThresh} number of mutations in any window of \code{windowSize} consecutive nucleotides (i.e. the sequence should be filtered); \code{FALSE} if otherwise. } \description{ \code{slideWindowSeq} determines whether an input sequence contains equal to or more than a given number of mutations in a given length of consecutive nucleotides (a "window") when compared to a germline sequence. } \examples{ # Use an entry in the example data for input and germline sequence data(ExampleDb, package="alakazam") in_seq <- ExampleDb[["sequence_alignment"]][100] germ_seq <- ExampleDb[["germline_alignment_d_mask"]][100] # Determine if in_seq has 6 or more mutations in 10 consecutive nucleotides slideWindowSeq(inputSeq=in_seq, germlineSeq=germ_seq, mutThresh=6, windowSize=10) slideWindowSeq(inputSeq="TCGTCGAAAA", germlineSeq="AAAAAAAAAA", mutThresh=6, windowSize=10) } \seealso{ \link{calcObservedMutations} is called by \code{slideWindowSeq} to identify observed mutations. See \link{slideWindowDb} for applying the sliding window approach on a \code{data.frame}. See \link{slideWindowTune} for parameter tuning for \code{mutThresh} and \code{windowSize}. } shazam/man/plotGmmThreshold.Rd0000644000176200001440000000457614470664105016116 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/DistToNearest.R \name{plotGmmThreshold} \alias{plotGmmThreshold} \title{Plot findThreshold results for the gmm method} \usage{ plotGmmThreshold( data, cross = NULL, xmin = NULL, xmax = NULL, breaks = NULL, binwidth = NULL, title = NULL, size = 1, silent = FALSE, ... ) } \arguments{ \item{data}{\link{GmmThreshold} object output by the \code{"gmm"} method of \link{findThreshold}.} \item{cross}{numeric vector of distances from \link{distToNearest} to draw as a histogram below the \code{data} histogram for comparison purposes.} \item{xmin}{minimum limit for plotting the x-axis. If \code{NULL} the limit will be set automatically.} \item{xmax}{maximum limit for plotting the x-axis. If \code{NULL} the limit will be set automatically.} \item{breaks}{number of breaks to show on the x-axis. If \code{NULL} the breaks will be set automatically.} \item{binwidth}{binwidth for the histogram. If \code{NULL} the binwidth will be set automatically.} \item{title}{string defining the plot title.} \item{size}{numeric value for lines in the plot.} \item{silent}{if \code{TRUE} do not draw the plot and just return the ggplot2 object; if \code{FALSE} draw the plot.} \item{...}{additional arguments to pass to ggplot2::theme.} } \value{ A ggplot object defining the plot. } \description{ \code{plotGmmThreshold} plots the results from \code{"gmm"} method of \link{findThreshold}, including the Gaussian distributions, input nearest neighbor distance histogram, and threshold selected. } \examples{ \donttest{ # Subset example data to one sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, sample_id == "-1h") # Use nucleotide Hamming distance and normalize by junction length db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # To find the threshold cut, call findThreshold function for "gmm" method. output <- findThreshold(db$dist_nearest, method="gmm", model="norm-norm", cutoff="opt") print(output) # Plot results plotGmmThreshold(output, binwidth=0.02) } } \seealso{ See \link{GmmThreshold} for the the input object definition and \link{findThreshold} for generating the input object. See \link{distToNearest} calculating nearest neighbor distances. } shazam/man/shazam.Rd0000644000176200001440000001275614470664105014104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Shazam.R \docType{package} \name{shazam} \alias{shazam} \title{The shazam package} \description{ Dramatic improvements in high-throughput sequencing technologies now enable large-scale characterization of Ig repertoires, defined as the collection of transmembrane antigen-receptor proteins located on the surface of T and B lymphocytes. The \code{shazam} package provides tools for advanced analysis of somatic hypermutation (SHM) in immunoglobulin (Ig) sequences. The key functions in \code{shazam}, broken down topic, are described below. } \section{Mutational profiling}{ \code{shazam} provides tools to quantify the extent and nature of SHM within full length V(D)J sequences as well as sub-regions (eg, FWR and CDR). Quantification of expected mutational loaded, under specific SHM targeting models, can also be performed along with model driven simulations of SHM. \itemize{ \item \link{collapseClones}: Build clonal consensus sequences. \item \link{consensusSequence}: Build a single consensus sequence. \item \link{observedMutations}: Compute observed mutation counts and frequencies. \item \link{expectedMutations}: Compute expected mutation frequencies. \item \link{shmulateSeq}: Simulate mutations in a single sequence. \item \link{shmulateTree}: Simulate mutations over a lineage tree. \item \link{setRegionBoundaries}: Extends a region definition to include CDR3 and FWR4. } } \section{SHM targeting models}{ Computational models and analyses of SHM have separated the process into two independent components: \enumerate{ \item A mutability model that defines where mutations occur. \item A nucleotide substitution model that defines the resulting mutation. } Collectively these are what form the targeting model of SHM. \code{shazam} provides empirically derived targeting models for both humans and mice, along with tools to build these mutability and substitution models from data. \itemize{ \item \link{createTargetingModel}: Build a 5-mer targeting model. \item \link{plotMutability}: Plot 5-mer mutability rates. \item \link{HH_S5F}: Human 5-mer SHM targeting model. \item \link{MK_RS5NF}: Mouse 5-mer SHM targeting model. } } \section{Quantification of selection pressure}{ Bayesian Estimation of Antigen-driven Selection in Ig Sequences is a novel method for quantifying antigen-driven selection in high-throughput Ig sequence data. Targeting models created using \code{shazam} can be used to estimate the null distribution of expected mutation frequencies used by BASELINe, providing measures of selection pressure informed by known AID targeting biases. \itemize{ \item \link{calcBaseline}: Calculate the BASELINe probability density functions (PDFs). \item \link{groupBaseline}: Combine PDFs from sequences grouped by biological or experimental relevance. \item \link{summarizeBaseline}: Compute summary statistics from BASELINe PDFs. \item \link{testBaseline}: Perform significance testing for the difference between BASELINe PDFs. \item \link{plotBaselineDensity}: Plot the probability density functions resulting from selection analysis. \item \link{plotBaselineSummary}: Plot summary stastistics resulting from selection analysis. } } \section{Mutational distance calculation}{ \code{shazam} provides tools to compute evolutionary distances between sequences or groups of sequences, which can leverage SHM targeting models. This information is particularly useful in understanding and defining clonal relationships. \itemize{ \item \link{findThreshold}: Identify clonal assignment threshold based on distances to nearest neighbors. \item \link{distToNearest}: Tune clonal assignment thresholds by calculating distances to nearest neighbors. \item \link{calcTargetingDistance}: Construct a nucleotide distance matrix from a 5-mer targeting model. } } \references{ \enumerate{ \item Hershberg U, et al. Improved methods for detecting selection by mutation analysis of Ig V region sequences. Int Immunol. 2008 20(5):683-94. \item Uduman M, et al. Detecting selection in immunoglobulin sequences. Nucleic Acids Res. 2011 39(Web Server issue):W499-504. (Corrections at http://selection.med.yale.edu/baseline/correction/) \item Yaari G, et al. Quantifying selection in high-throughput immunoglobulin sequencing data sets. Nucleic Acids Res. 2012 40(17):e134. \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4:358. \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of Immunology, 197(9), 3566-3574. } } shazam/man/minNumMutationsTune.Rd0000644000176200001440000000652314470664105016617 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{minNumMutationsTune} \alias{minNumMutationsTune} \title{Parameter tuning for minNumMutations} \usage{ minNumMutationsTune(subCount, minNumMutationsRange) } \arguments{ \item{subCount}{\code{data.frame} returned by \link{createSubstitutionMatrix} with \code{numMutationsOnly=TRUE}.} \item{minNumMutationsRange}{a number or a vector indicating the value or range of values of \code{minNumMutations} to try.} } \value{ A 3xn \code{matrix}, where n is the number of trial values of \code{minNumMutations} supplied in \code{minNumMutationsRange}. Each column corresponds to a value in \code{minNumMutationsRange}. The rows correspond to the number of 5-mers for which substitution rates would be computed directly using the 5-mer itself (\code{"5mer"}), using its inner 3-mer (\code{"3mer"}), and using the central 1-mer (\code{"1mer"}), respectively. } \description{ \code{minNumMutationsTune} helps with picking a threshold value for \code{minNumMutations} in \link{createSubstitutionMatrix} by tabulating the number of 5-mers for which substitution rates would be computed directly or inferred at various threshold values. } \details{ At a given threshold value of \code{minNumMutations}, for a given 5-mer, if the total number of mutations is greater than the threshold and there are mutations to every other base, substitution rates are computed directly for the 5-mer using its mutations. Otherwise, mutations from 5-mers with the same inner 3-mer as the 5-mer of interest are aggregated. If the number of such mutations is greater than the threshold and there are mutations to every other base, these mutations are used for inferring the substitution rates for the 5-mer of interest; if not, mutations from all 5-mers with the same center nucleotide are aggregated and used for inferring the substitution rates for the 5-mer of interest (i.e. the 1-mer model). } \examples{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") # Count the number of mutations per 5-mer subCount <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", model="s", multipleMutation="independent", returnModel="5mer", numMutationsOnly=TRUE) # Tune minNumMutations minNumMutationsTune(subCount, seq(from=10, to=80, by=10)) } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See argument \code{numMutationsOnly} in \link{createSubstitutionMatrix} for generating the required input \code{data.frame} \code{subCount}. See argument \code{minNumMutations} in \link{createSubstitutionMatrix} for what it does. } shazam/man/makeDegenerate5merMut.Rd0000644000176200001440000000405014470664106016766 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{makeDegenerate5merMut} \alias{makeDegenerate5merMut} \title{Make a degenerate 5-mer mutability model based on a 1-mer mutability model} \usage{ makeDegenerate5merMut(mut1mer, extended = FALSE) } \arguments{ \item{mut1mer}{a named vector of length 4 containing (normalized) mutability rates. Names should correspond to nucleotides, which should include "A", "T", "G", and "C" (case-insensitive).} \item{extended}{whether to return the unextended (\code{extended=FALSE}) or extended (\code{extended=TRUE}) 5-mer mutability model. Default is \code{FALSE}.} } \value{ For \code{extended=FALSE}, a vector of length 1024. The vector returned is normalized. For \code{extended=TRUE}, a vector of length 3125. } \description{ \code{makeDegenerate5merMut} populates mutability rates from a 1-mer mutability model into 5-mers with corresponding central 1-mers. } \details{ As a concrete example, consider a 1-mer mutability model in which mutability rates of "A", "T", "G", and "C" are, respectively, 0.14, 0.23, 0.31, and 0.32. In the resultant degenerate 5-mer mutability model, all the 5-mers that have an "A" as their central 1-mer would have mutability rate of 0.14/256, where 256 is the number of such 5-mers. When \code{extended=TRUE}, \code{extendMutabilityMatrix} is called to extend the mutability vector of length 1024 into a vector of length 3125. } \examples{ # Make a degenerate 5-mer model (length of 1024) based on a 1-mer model example1merMut <- c(A=0.2, T=0.1, C=0.4, G=0.3) degenerate5merMut <- makeDegenerate5merMut(mut1mer = example1merMut) # Look at a few 5-mers degenerate5merMut[c("AAAAT", "AACAT", "AAGAT", "AATAT")] # Normalized sum(degenerate5merMut) } \seealso{ See \link{makeAverage1merMut} for making a 1-mer mutability model by taking the average of a 5-mer mutability model. See \link{extendMutabilityMatrix} for extending the mutability vector. } shazam/man/calculateMutability.Rd0000644000176200001440000000201614470664106016607 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{calculateMutability} \alias{calculateMutability} \title{Calculate total mutability} \usage{ calculateMutability(sequences, model = HH_S5F, progress = FALSE) } \arguments{ \item{sequences}{character vector of sequences.} \item{model}{\link{TargetingModel} object with mutation likelihood information.} \item{progress}{if \code{TRUE} print a progress bar.} } \value{ Numeric vector with a total mutability score for each sequence. } \description{ \code{calculateMutability} calculates the total (summed) mutability for a set of sequences based on a 5-mer nucleotide mutability model. } \examples{ \donttest{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") # Calculate mutability of germline sequences using \link{HH_S5F} model mutability <- calculateMutability(sequences=db[["germline_alignment_d_mask"]], model=HH_S5F) } } shazam/man/slideWindowTunePlot.Rd0000644000176200001440000001277014470664105016600 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Deprecated.R \name{slideWindowTunePlot} \alias{slideWindowTunePlot} \title{slideWindowTunePlot - plotSlideWindowTune backward compatability} \usage{ slideWindowTunePlot( tuneList, plotFiltered = c(TRUE, FALSE, NULL, "filtered", "remaining", "per_mutation"), percentage = FALSE, jitter.x = FALSE, jitter.x.amt = 0.1, jitter.y = FALSE, jitter.y.amt = 0.1, pchs = 1, ltys = 2, cols = 1, plotLegend = TRUE, legendPos = "topright", legendHoriz = FALSE, legendCex = 1, title = NULL, returnRaw = FALSE ) } \arguments{ \item{tuneList}{a list of logical matrices returned by \link{slideWindowTune}.} \item{plotFiltered}{whether to plot the number of filtered (\code{TRUE} or \code{filtered}), or remaining (FALSE or remaining) sequences for each mutation threshold. Use \code{NULL} or \code{per_mutation} to plot the number of sequences at each mutation value. Default is \code{TRUE}.} \item{percentage}{whether to plot on the y-axis the percentage of filtered sequences (as opposed to the absolute number). Default is \code{FALSE}.} \item{jitter.x}{whether to jitter x-axis values. Default is \code{FALSE}.} \item{jitter.x.amt}{amount of jittering to be applied on x-axis values if \code{jitter.x=TRUE}. Default is 0.1.} \item{jitter.y}{whether to jitter y-axis values. Default is \code{FALSE}.} \item{jitter.y.amt}{amount of jittering to be applied on y-axis values if \code{jitter.y=TRUE}. Default is 0.1.} \item{pchs}{point types to pass on to \link{plot}.} \item{ltys}{line types to pass on to \link{plot}.} \item{cols}{colors to pass on to \link{plot}.} \item{plotLegend}{whether to plot legend. Default is \code{TRUE}.} \item{legendPos}{position of legend to pass on to \link{legend}. Can be either a numeric vector specifying x-y coordinates, or one of \code{"topright"}, \code{"center"}, etc. Default is \code{"topright"}.} \item{legendHoriz}{whether to make legend horizontal. Default is \code{FALSE}.} \item{legendCex}{numeric values by which legend should be magnified relative to 1.} \item{title}{plot main title. Default is NULL (no title)} \item{returnRaw}{Return a data.frame with sequence counts (TRUE) or a plot. Default is \code{FALSE}.} } \description{ Wrapper function for \link{plotSlideWindowTune} } \details{ For each \code{windowSize}, if \code{plotFiltered=TRUE}, the x-axis represents a mutation threshold range, and the y-axis the number of sequences that have at least that number of mutations. If \code{plotFiltered=TRUE}, the y-axis represents the number of sequences that have less mutations than the mutation threshold range. For the same window size, a sequence can be included in the counts for different mutation thresholds. For example, sequence "CCACCAAAA" with germline "AAAAAAAAA" has 4 mutations. This sequence has at least 2 mutations and at least 3 mutations, in a window of size 4. the sequence will be included in the sequence count for mutation thresholds 2 and 3. If \code{plotFiltered=TRUE}, the sequences are counted only once for each window size, at their largest mutation threshold. The above example sequence would be included in the sequence count for mutation threshold 3. When plotting, a user-defined \code{amount} of jittering can be applied on values plotted on either axis or both axes via adjusting \code{jitter.x}, \code{jitter.y}, \code{jitter.x.amt} and \code{jitter.y.amt}. This may be help with visually distinguishing lines for different window sizes in case they are very close or identical to each other. If plotting percentages (\code{percentage=TRUE}) and using jittering on the y-axis values (\code{jitter.y=TRUE}), it is strongly recommended that \code{jitter.y.amt} be set very small (e.g. 0.01). \code{NA} for a combination of \code{mutThresh} and \code{windowSize} where \code{mutThresh} is greater than \code{windowSize} will not be plotted. } \examples{ # Use an entry in the example data for input and germline sequence data(ExampleDb, package="alakazam") # Try out thresholds of 2-4 mutations in window sizes of 3-5 nucleotides # on a subset of ExampleDb tuneList <- slideWindowTune(db = ExampleDb[1:10, ], mutThreshRange = 2:4, windowSizeRange = 3:5, verbose = FALSE) # Visualize # Plot numbers of sequences filtered without jittering y-axis values slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered=TRUE, jitter.y=FALSE) # Notice that some of the lines overlap # Jittering could help slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered=TRUE, jitter.y=TRUE) # Plot numbers of sequences remaining instead of filtered slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered=FALSE, jitter.y=TRUE, legendPos="bottomright") # Plot percentages of sequences filtered with a tiny amount of jittering slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, plotFiltered=TRUE, percentage=TRUE, jitter.y=TRUE, jitter.y.amt=0.01) } \seealso{ See \link{slideWindowTune} for how to get \code{tuneList}. See \link{jitter} for use of \code{amount} of jittering. } shazam/man/createMutabilityMatrix.Rd0000644000176200001440000001206714470664105017310 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{createMutabilityMatrix} \alias{createMutabilityMatrix} \title{Builds a mutability model} \usage{ createMutabilityMatrix( db, substitutionModel, model = c("s", "rs"), sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", vCallColumn = "v_call", multipleMutation = c("independent", "ignore"), minNumSeqMutations = 500, numSeqMutationsOnly = FALSE ) } \arguments{ \item{db}{data.frame containing sequence data.} \item{substitutionModel}{matrix of 5-mer substitution rates built by \link{createSubstitutionMatrix}. Note, this model will only impact mutability scores when \code{model="s"} (using only silent mutations).} \item{model}{type of model to create. The default model, "s", builds a model by counting only silent mutations. \code{model="s"} should be used for data that includes functional sequences. Setting \code{model="rs"} creates a model by counting both replacement and silent mutations and may be used on fully non-functional sequence data sets.} \item{sequenceColumn}{name of the column containing IMGT-gapped sample sequences.} \item{germlineColumn}{name of the column containing IMGT-gapped germline sequences.} \item{vCallColumn}{name of the column containing the V-segment allele call.} \item{multipleMutation}{string specifying how to handle multiple mutations occuring within the same 5-mer. If \code{"independent"} then multiple mutations within the same 5-mer are counted indepedently. If \code{"ignore"} then 5-mers with multiple mutations are excluded from the total mutation tally.} \item{minNumSeqMutations}{minimum number of mutations in sequences containing each 5-mer to compute the mutability rates. If the number is smaller than this threshold, the mutability for the 5-mer will be inferred. Default is 500. Not required if \code{numSeqMutationsOnly=TRUE}.} \item{numSeqMutationsOnly}{when \code{TRUE}, return only a vector counting the number of observed mutations in sequences containing each 5-mer. This option can be used for parameter tuning for \code{minNumSeqMutations} during preliminary analysis using \link{minNumSeqMutationsTune}. Default is \code{FALSE}.} } \value{ When \code{numSeqMutationsOnly} is \code{FALSE}, a \code{MutabilityModel} containing a named numeric vector of 1024 normalized mutability rates for each 5-mer motif with names defining the 5-mer nucleotide sequence. When \code{numSeqMutationsOnly} is \code{TRUE}, a named numeric vector of length 1024 counting the number of observed mutations in sequences containing each 5-mer. } \description{ \code{createMutabilityMatrix} builds a 5-mer nucleotide mutability model by counting the number of mutations occuring in the center position for all 5-mer motifs. } \details{ \strong{Caution: The targeting model functions do NOT support ambiguous characters in their inputs. You MUST make sure that your input and germline sequences do NOT contain ambiguous characters (especially if they are clonal consensuses returned from \code{collapseClones}).} } \examples{ \donttest{ # Subset example data to 50 sequences of one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:50,] # Create model using only silent mutations sub_model <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call",model="s") mut_model <- createMutabilityMatrix(db, sub_model, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", minNumSeqMutations=200, numSeqMutationsOnly=FALSE) # View top 5 mutability estimates head(sort(mut_model, decreasing=TRUE), 5) # View the number of S mutations used for estimating mutabilities mut_model@numMutS # Count the number of mutations in sequences containing each 5-mer mut_count <- createMutabilityMatrix(db, sub_model, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", numSeqMutationsOnly=TRUE) } } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ \link{MutabilityModel}, \link{extendMutabilityMatrix}, \link{createSubstitutionMatrix}, \link{createTargetingMatrix}, \link{createTargetingModel}, \link{minNumSeqMutationsTune} } shazam/man/summarizeBaseline.Rd0000644000176200001440000000565214470664105016275 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{summarizeBaseline} \alias{summarizeBaseline} \title{Calculate BASELINe summary statistics} \usage{ summarizeBaseline(baseline, returnType = c("baseline", "df"), nproc = 1) } \arguments{ \item{baseline}{\code{Baseline} object returned by \link{calcBaseline} containing annotations and BASELINe posterior probability density functions (PDFs) for each sequence.} \item{returnType}{One of \code{c("baseline", "df")} defining whether to return a \code{Baseline} object ("baseline") with an updated \code{stats} slot or a data.frame ("df") of summary statistics.} \item{nproc}{number of cores to distribute the operation over. If \code{nproc} = 0 then the \code{cluster} has already been set and will not be reset.} } \value{ Either a modified \code{Baseline} object or data.frame containing the mean BASELINe selection strength, its 95\% confidence intervals, and a p-value for the presence of selection. } \description{ \code{summarizeBaseline} calculates BASELINe statistics such as the mean selection strength (mean Sigma), the 95\% confidence intervals and p-values for the presence of selection. } \details{ The returned p-value can be either positive or negative. Its magnitude (without the sign) should be interpreted as per normal. Its sign indicates the direction of the selection detected. A positive p-value indicates positive selection, whereas a negative p-value indicates negative selection. } \examples{ \donttest{ # Subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHG") set.seed(112) db <- dplyr::slice_sample(db, n=100) # Collapse clones db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # Calculate BASELINe baseline <- calcBaseline(db, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc = 1) # Grouping the PDFs by the sample annotation grouped <- groupBaseline(baseline, groupBy="sample_id") # Get a data.frame of the summary statistics stats <- summarizeBaseline(grouped, returnType="df") } } \references{ \enumerate{ \item Uduman M, et al. Detecting selection in immunoglobulin sequences. Nucleic Acids Res. 2011 39(Web Server issue):W499-504. } } \seealso{ See \link{calcBaseline} for generating \code{Baseline} objects and \link{groupBaseline} for convolving groups of BASELINe PDFs. } shazam/man/writeTargetingDistance.Rd0000644000176200001440000000210614470664106017260 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{writeTargetingDistance} \alias{writeTargetingDistance} \title{Write targeting model distances to a file} \usage{ writeTargetingDistance(model, file) } \arguments{ \item{model}{\link{TargetingModel} object with mutation likelihood information.} \item{file}{name of file to write.} } \description{ \code{writeTargetingDistance} writes a 5-mer targeting distance matrix to a tab-delimited file. } \details{ The targeting distance write as a tab-delimited 5x3125 matrix. Rows define the mutated nucleotide at the center of each 5-mer, one of \code{c("A", "C", "G", "T", "N")}, and columns define the complete 5-mer of the unmutated nucleotide sequence. \code{NA} values in the distance matrix are replaced with distance 0. } \examples{ \dontrun{ # Write HS5F targeting model to working directory as hs5f.tab writeTargetingDistance(HH_S5F, "hh_s5f.tsv") } } \seealso{ Takes as input a \link{TargetingModel} object and calculates distances using \link{calcTargetingDistance}. } shazam/man/findThreshold.Rd0000644000176200001440000001266414470664105015414 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/DistToNearest.R \name{findThreshold} \alias{findThreshold} \title{Find distance threshold} \usage{ findThreshold( distances, method = c("density", "gmm"), edge = 0.9, cross = NULL, subsample = NULL, model = c("gamma-gamma", "gamma-norm", "norm-gamma", "norm-norm"), cutoff = c("optimal", "intersect", "user"), sen = NULL, spc = NULL, progress = FALSE ) } \arguments{ \item{distances}{numeric vector containing nearest neighbor distances.} \item{method}{string defining the method to use for determining the optimal threshold. One of \code{"gmm"} or \code{"density"}. See Details for methodological descriptions.} \item{edge}{upper range as a fraction of the data density to rule initialization of Gaussian fit parameters. Default value is 90% of the entries (0.9). Applies only when \code{method="density"}. .} \item{cross}{supplementary nearest neighbor distance vector output from \link{distToNearest} for initialization of the Gaussian fit parameters. Applies only when \code{method="gmm"}.} \item{subsample}{maximum number of distances to subsample to before threshold detection.} \item{model}{allows the user to choose among four possible combinations of fitting curves: \code{"norm-norm"}, \code{"norm-gamma"}, \code{"gamma-norm"}, and \code{"gamma-gamma"}. Applies only when \code{method="gmm"}.} \item{cutoff}{method to use for threshold selection: the optimal threshold \code{"opt"}, the intersection point of the two fitted curves \code{"intersect"}, or a value defined by user for one of the sensitivity or specificity \code{"user"}. Applies only when \code{method="gmm"}.} \item{sen}{sensitivity required. Applies only when \code{method="gmm"} and \code{cutoff="user"}.} \item{spc}{specificity required. Applies only when \code{method="gmm"} and \code{cutoff="user"}.} \item{progress}{if \code{TRUE} print a progress bar.} } \value{ \itemize{ \item \code{"gmm"} method: Returns a \link{GmmThreshold} object including the \code{threshold} and the function fit parameters, i.e. mixing weight, mean, and standard deviation of a Normal distribution, or mixing weight, shape and scale of a Gamma distribution. \item \code{"density"} method: Returns a \link{DensityThreshold} object including the optimum \code{threshold} and the density fit parameters. } } \description{ \code{findThreshold} automatically determines an optimal threshold for clonal assignment of Ig sequences using a vector of nearest neighbor distances. It provides two alternative methods using either a Gamma/Gaussian Mixture Model fit (\code{method="gmm"}) or kernel density fit (\code{method="density"}). } \details{ \itemize{ \item \code{"gmm"}: Performs a maximum-likelihood fitting procedure, for learning the parameters of two mixture univariate, either Gamma or Gaussian, distributions which fit the bimodal distribution entries. Retrieving the fit parameters, it then calculates the optimum threshold \code{method="optimal"}, where the average of the sensitivity plus specificity reaches its maximum. In addition, the \code{findThreshold} function is also able to calculate the intersection point (\code{method="intersect"}) of the two fitted curves and allows the user to invoke its value as the cut-off point, instead of optimal point. \item \code{"density"}: Fits a binned approximation to the ordinary kernel density estimate to the nearest neighbor distances after determining the optimal bandwidth for the density estimate via least-squares cross-validation of the 4th derivative of the kernel density estimator. The optimal threshold is set as the minimum value in the valley in the density estimate between the two modes of the distribution. } } \note{ Visually inspecting the resulting distribution fits is strongly recommended when using either fitting method. Empirical observations imply that the bimodality of the distance-to-nearest distribution is detectable for a minimum of 1,000 distances. Larger numbers of distances will improve the fitting procedure, although this can come at the expense of higher computational demands. } \examples{ \donttest{ # Subset example data to 50 sequences, one sample and isotype as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, sample_id == "-1h" & c_call=="IGHG")[1:50,] # Use nucleotide Hamming distance and normalize by junction length db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # Find threshold using the "gmm" method with user defined specificity output <- findThreshold(db$dist_nearest, method="gmm", model="gamma-gamma", cutoff="user", spc=0.99) plot(output, binwidth=0.02, title=paste0(output@model, " loglk=", output@loglk)) print(output) } } \seealso{ See \link{distToNearest} for generating the nearest neighbor distance vectors. See \link{plotGmmThreshold} and \link{plotDensityThreshold} for plotting output. } shazam/man/shmulateTree.Rd0000644000176200001440000000617714470664105015263 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Shmulate.R \name{shmulateTree} \alias{shmulateTree} \title{Simulate mutations in a lineage tree} \usage{ shmulateTree( sequence, graph, targetingModel = HH_S5F, field = NULL, exclude = NULL, junctionWeight = NULL, start = 1, end = nchar(sequence) ) } \arguments{ \item{sequence}{string defining the MRCA sequence to seed mutations from.} \item{graph}{\code{igraph} object defining the seed lineage tree, with vertex annotations, whose edges are to be recreated.} \item{targetingModel}{5-mer \link{TargetingModel} object to be used for computing probabilities of mutations at each position. Defaults to \link{HH_S5F}.} \item{field}{annotation to use for both unweighted path length exclusion and consideration as the MRCA node. If \code{NULL} do not exclude any nodes.} \item{exclude}{vector of annotation values in \code{field} to exclude from potential MRCA set. If \code{NULL} do not exclude any nodes. Has no effect if \code{field=NULL}.} \item{junctionWeight}{fraction of the nucleotide sequence that is within the junction region. When specified this adds a proportional number of mutations to the immediate offspring nodes of the MRCA. Requires a value between 0 and 1. If \code{NULL} then edge weights are unmodified from the input \code{graph}.} \item{start}{Initial position in \code{sequence} where mutations can be introduced. Default: 1} \item{end}{Last position in \code{sequence} where mutations can be introduced. Default: last position (sequence length).} } \value{ A \code{data.frame} of simulated sequences with columns: \itemize{ \item \code{name}: name of the corresponding node in the input \code{graph}. \item \code{sequence}: mutated sequence. \item \code{distance}: Hamming distance of the mutated sequence from the seed \code{sequence}. } } \description{ \code{shmulateTree} returns a set of simulated sequences generated from an input sequence and a lineage tree. The input sequence is used to replace the most recent common ancestor (MRCA) node of the \code{igraph} object defining the lineage tree. Sequences are then simulated with mutations corresponding to edge weights in the tree. Sequences will not be generated for groups of nodes that are specified to be excluded. } \examples{ # Load example lineage and define example MRCA data(ExampleTrees, package="alakazam") graph <- ExampleTrees[[17]] sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATAGTTTA" # Simulate using the default human 5-mer targeting model shmulateTree(sequence, graph) # Simulate using the mouse 5-mer targeting model # Exclude nodes without a sample identifier # Add 20\% mutation rate to the immediate offsprings of the MRCA shmulateTree(sequence, graph, targetingModel=MK_RS5NF, field="sample_id", exclude=NA, junctionWeight=0.2) } \seealso{ See \link{shmulateSeq} for imposing mutations on a single sequence. See \link{HH_S5F} and \link{MK_RS5NF} for predefined \link{TargetingModel} objects. } shazam/man/minNumSeqMutationsTune.Rd0000644000176200001440000000637314470664106017274 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{minNumSeqMutationsTune} \alias{minNumSeqMutationsTune} \title{Parameter tuning for minNumSeqMutations} \usage{ minNumSeqMutationsTune(mutCount, minNumSeqMutationsRange) } \arguments{ \item{mutCount}{a \code{vector} of length 1024 returned by \link{createMutabilityMatrix} with \code{numSeqMutationsOnly=TRUE}.} \item{minNumSeqMutationsRange}{a number or a vector indicating the value or the range of values of \code{minNumSeqMutations} to try.} } \value{ A 2xn \code{matrix}, where n is the number of trial values of \code{minNumSeqMutations} supplied in \code{minNumSeqMutationsRange}. Each column corresponds to a value in \code{minNumSeqMutationsRange}. The rows correspond to the number of 5-mers for which mutability would be computed directly (\code{"measured"}) and inferred (\code{"inferred"}), respectively. } \description{ \code{minNumSeqMutationsTune} helps with picking a threshold value for \code{minNumSeqMutations} in \link{createMutabilityMatrix} by tabulating the number of 5-mers for which mutability would be computed directly or inferred at various threshold values. } \details{ At a given threshold value of \code{minNumSeqMutations}, for a given 5-mer, if the total number of mutations is greater than the threshold, mutability is computed directly. Otherwise, mutability is inferred. } \examples{ \donttest{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") set.seed(112) db <- dplyr::slice_sample(db, n=75) # Create model using only silent mutations sub <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", model="s", multipleMutation="independent", returnModel="5mer", numMutationsOnly=FALSE, minNumMutations=20) # Count the number of mutations in sequences containing each 5-mer mutCount <- createMutabilityMatrix(db, substitutionModel = sub, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", model="s", multipleMutation="independent", numSeqMutationsOnly=TRUE) # Tune minNumSeqMutations minNumSeqMutationsTune(mutCount, seq(from=100, to=300, by=50)) } } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See argument \code{numSeqMutationsOnly} in \link{createMutabilityMatrix} for generating the required input \code{vector} \code{mutCount}. See argument \code{minNumSeqMutations} in \link{createMutabilityMatrix} for what it does. } shazam/man/HH_S1F.Rd0000644000176200001440000000203214470664105013553 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{HH_S1F} \alias{HH_S1F} \title{Human heavy chain, silent, 1-mer, functional substitution model.} \format{ A 4x4 matrix of nucleotide substitution rates. The rates are normalized, therefore each row sums up to 1. } \usage{ HH_S1F } \description{ 1-mer substitution model of somatic hypermutation based on analysis of silent mutations in functional heavy chain Ig sequences from Homo sapiens. } \note{ \code{HH_S1F} replaces \code{HS1FDistance} in versions of SHazaM prior to 0.1.5. } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See \link{HKL_S1F} for the human light chain 1-mer substitution model and \link{MK_RS1NF} for the mouse light chain 1-mer substitution model. } \keyword{datasets} shazam/man/MK_RS1NF.Rd0000644000176200001440000000222714470664105014031 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{MK_RS1NF} \alias{MK_RS1NF} \title{Mouse kappa chain, replacement and silent, 1-mer, non-functional substitution model.} \format{ A 4x4 matrix of nucleotide substitution rates. The rates are normalized, therefore each row sums up to 1. } \usage{ MK_RS1NF } \description{ 1-mer substitution model of somatic hypermutation based on analysis of replacement and silent mutations in non-functional kappa light chain Ig sequences from NP-immunized Mus musculus. } \note{ \code{MK_RS1NF} replaces \code{M1NDistance} from versions of SHazaM prior to 0.1.5. } \references{ \enumerate{ \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of Immunology, 197(9), 3566-3574. } } \seealso{ See \link{HH_S1F} for the human heavy chain 1-mer substitution model and \link{HKL_S1F} for the human light chain 1-mer substitution model. } \keyword{datasets} shazam/man/createSubstitutionMatrix.Rd0000644000176200001440000001344514470664105017702 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{createSubstitutionMatrix} \alias{createSubstitutionMatrix} \title{Builds a substitution model} \usage{ createSubstitutionMatrix( db, model = c("s", "rs"), sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", vCallColumn = "v_call", multipleMutation = c("independent", "ignore"), returnModel = c("5mer", "1mer", "1mer_raw"), minNumMutations = 50, numMutationsOnly = FALSE ) } \arguments{ \item{db}{data.frame containing sequence data.} \item{model}{type of model to create. The default model, "s", builds a model by counting only silent mutations. \code{model="s"} should be used for data that includes functional sequences. Setting \code{model="rs"} creates a model by counting both replacement and silent mutations and may be used on fully non-functional sequence data sets.} \item{sequenceColumn}{name of the column containing IMGT-gapped sample sequences.} \item{germlineColumn}{name of the column containing IMGT-gapped germline sequences.} \item{vCallColumn}{name of the column containing the V-segment allele call.} \item{multipleMutation}{string specifying how to handle multiple mutations occuring within the same 5-mer. If \code{"independent"} then multiple mutations within the same 5-mer are counted indepedently. If \code{"ignore"} then 5-mers with multiple mutations are excluded from the total mutation tally.} \item{returnModel}{string specifying what type of model to return; one of \code{c("5mer", "1mer", "1mer_raw")}. If \code{"5mer"} (the default) then a 5-mer nucleotide context model is returned. If \code{"1mer"} or \code{"1mer_raw"} then a single nucleotide substitution matrix (no context) is returned; where \code{"1mer_raw"} is the unnormalized version of the \code{"1mer"} model. Note, neither 1-mer model may be used as input to \link{createMutabilityMatrix}.} \item{minNumMutations}{minimum number of mutations required to compute the 5-mer substitution rates. If the number of mutations for a 5-mer is below this threshold, its substitution rates will be estimated from neighboring 5-mers. Default is 50. Not required if \code{numMutationsOnly=TRUE}.} \item{numMutationsOnly}{when \code{TRUE}, return counting information on the number of mutations for each 5-mer, instead of building a substitution matrix. This option can be used for parameter tuning for \code{minNumMutations} during preliminary analysis. Default is \code{FALSE}. Only applies when \code{returnModel} is set to \code{"5mer"}. The \code{data.frame} returned when this argument is \code{TRUE} can serve as the input for \link{minNumMutationsTune}.} } \value{ For \code{returnModel = "5mer"}: When \code{numMutationsOnly} is \code{FALSE}, a 4x1024 matrix of column normalized substitution rates for each 5-mer motif with row names defining the center nucleotide, one of \code{c("A", "C", "G", "T")}, and column names defining the 5-mer nucleotide sequence. When \code{numMutationsOnly} is \code{TRUE}, a 1024x4 data frame with each row providing information on counting the number of mutations for a 5-mer. Columns are named \code{fivemer.total}, \code{fivemer.every}, \code{inner3.total}, and \code{inner3.every}, corresponding to, respectively, the total number of mutations when counted as a 5-mer, whether there is mutation to every other base when counted as a 5-mer, the total number of mutations when counted as an inner 3-mer, and whether there is mutation to every other base when counted as an inner 3-mer. For \code{returnModel = "1mer"} or \code{"1mer_raw"}: a 4x4 normalized or un-normalized 1-mer substitution matrix respectively. } \description{ \code{createSubstitutionMatrix} builds a 5-mer nucleotide substitution model by counting the number of substitution mutations occuring in the center position for all 5-mer motifs. } \details{ \strong{Caution: The targeting model functions do NOT support ambiguous characters in their inputs. You MUST make sure that your input and germline sequences do NOT contain ambiguous characters (especially if they are clonal consensuses returned from \code{collapseClones}).} } \examples{ \donttest{ # Subset example data to one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:25,] # Count the number of mutations per 5-mer subCount <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", model="s", multipleMutation="independent", returnModel="5mer", numMutationsOnly=TRUE) # Create model using only silent mutations sub <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", model="s", multipleMutation="independent", returnModel="5mer", numMutationsOnly=FALSE, minNumMutations=20) } } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ \link{extendSubstitutionMatrix}, \link{createMutabilityMatrix}, \link{createTargetingMatrix}, \link{createTargetingModel}, \link{minNumMutationsTune}. } shazam/man/HKL_S5F.Rd0000644000176200001440000000201014470664105013672 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{data} \name{HKL_S5F} \alias{HKL_S5F} \title{Human kappa and lambda light chain, silent, 5-mer, functional targeting model.} \format{ A \link{TargetingModel} object. } \usage{ HKL_S5F } \description{ 5-mer model of somatic hypermutation targeting based on analysis of silent mutations in functional kappa and lambda light chain Ig sequences from Homo sapiens. } \references{ \enumerate{ \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of Immunology, 197(9), 3566-3574. } } \seealso{ See \link{HH_S5F} for the human heavy chain 5-mer targeting model; \link{MK_RS5NF} for the mouse kappa light chain 5-mer targeting model; and \link{U5N} for the uniform 5-mer null targeting model. } \keyword{datasets} shazam/man/MutabilityModel-class.Rd0000644000176200001440000000204214470664105017013 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \docType{class} \name{MutabilityModel-class} \alias{MutabilityModel-class} \alias{MutabilityModel} \alias{print,MutabilityModel-method} \alias{MutabilityModel-method} \alias{as.data.frame,MutabilityModel-method} \title{S4 class defining a mutability model} \usage{ \S4method{print}{MutabilityModel}(x) \S4method{as.data.frame}{MutabilityModel}(x) } \arguments{ \item{x}{\code{MutabilityModel} object.} } \description{ \code{MutabilityModel} defines a data structure for the 5-mer motif-based SHM targeting mutability model. } \section{Slots}{ \describe{ \item{\code{.Data}}{numeric vector containing 5-mer mutability estimates} \item{\code{source}}{character vector annotating whether the mutability was inferred or directly measured.} \item{\code{numMutS}}{a number indicating the number of silent mutations used for estimating mutability} \item{\code{numMutR}}{a number indicating the number of replacement mutations used for estimating mutability} }} shazam/man/createMutationDefinition.Rd0000644000176200001440000000265414477634455017626 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationDefinitions.R \name{createMutationDefinition} \alias{createMutationDefinition} \title{Creates a MutationDefinition} \usage{ createMutationDefinition(name, classes, description = "", citation = "") } \arguments{ \item{name}{name of the mutation definition.} \item{classes}{named character vectors with single-letter amino acid codes as names and amino acid classes as values, with \code{NA} assigned to set of characters \code{c("X", "*", "-", ".")}. Replacement (R) is be defined as a change in amino acid class and silent (S) as no change in class.} \item{description}{description of the mutation definition and its source data.} \item{citation}{publication source.} } \value{ A \code{MutationDefinition} object. } \description{ \code{createMutationDefinition} creates a \code{MutationDefinition}. } \examples{ # Define hydropathy classes suppressPackageStartupMessages(library(alakazam)) hydropathy <- list(hydrophobic=c("A", "I", "L", "M", "F", "W", "V"), hydrophilic=c("R", "N", "D", "C", "Q", "E", "K"), neutral=c("G", "H", "P", "S", "T", "Y")) chars <- unlist(hydropathy, use.names=FALSE) classes <- setNames(translateStrings(chars, hydropathy), chars) # Create hydropathy mutation definition md <- createMutationDefinition("Hydropathy", classes) } \seealso{ See \link{MutationDefinition} for the return object. } shazam/man/Baseline-class.Rd0000644000176200001440000000643614470664105015444 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \docType{class} \name{Baseline-class} \alias{Baseline-class} \alias{Baseline} \alias{plot,Baseline,character-method} \alias{Baseline-method} \alias{summary,Baseline-method} \title{S4 class defining a BASELINe (selection) object} \usage{ \S4method{plot}{Baseline,character}(x, y, ...) \S4method{summary}{Baseline}(object, nproc = 1) } \arguments{ \item{x}{\code{Baseline} object.} \item{y}{name of the column in the \code{db} slot of \code{baseline} containing primary identifiers.} \item{...}{arguments to pass to \link{plotBaselineDensity}.} \item{object}{\code{Baseline} object.} \item{nproc}{number of cores to distribute the operation over.} } \description{ \code{Baseline} defines a common data structure the results of selection analysis using the BASELINe method. } \section{Slots}{ \describe{ \item{\code{description}}{\code{character} providing general information regarding the sequences, selection analysis and/or object.} \item{\code{db}}{\code{data.frame} containing annotation information about the sequences and selection results.} \item{\code{regionDefinition}}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences.} \item{\code{testStatistic}}{\code{character} indicating the statistical framework used to test for selection. For example, \code{"local"} or \code{"focused"}.} \item{\code{regions}}{\code{character} vector defining the regions the BASELINe analysis was carried out on. For \code{"cdr"} and \code{"fwr"} or \code{"cdr1"}, \code{"cdr2"}, \code{"cdr3"}, etc.} \item{\code{numbOfSeqs}}{\code{matrix} of dimensions \code{r x c} containing the number of sequences or PDFs in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{\code{binomK}}{\code{matrix} of dimensions \code{r x c} containing the number of successes in the binomial trials in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{\code{binomN}}{\code{matrix} of dimensions \code{r x c} containing the total number of trials in the binomial in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{\code{binomP}}{\code{matrix} of dimensions \code{r x c} containing the probability of success in one binomial trial in each region, where:\cr \code{r} = number of rows = number of groups or sequences.\cr \code{c} = number of columns = number of regions.} \item{\code{pdfs}}{\code{list} of matrices containing PDFs with one item for each defined region (e.g. \code{cdr} and \code{fwr}). Matrices have dimensions \code{r x c} dementions, where:\cr \code{r} = number of rows = number of sequences or groups. \cr \code{c} = number of columns = length of the PDF (default 4001).} \item{\code{stats}}{\code{data.frame} of BASELINe statistics, including: mean selection strength (mean Sigma), 95\% confidence intervals, and p-values with positive signs for the presence of positive selection and/or p-values with negative signs for the presence of negative selection.} }} \seealso{ See \link{summarizeBaseline} for more information on \code{@stats}. } shazam/man/convertNumbering.Rd0000644000176200001440000000207414470664105016140 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ConvertNumbering.R \name{convertNumbering} \alias{convertNumbering} \title{convertNumbering: IMGT-Kabat number conversion} \usage{ convertNumbering(locus, from, to, calls) } \arguments{ \item{locus}{string indicating heavy ("IGH") or light chains ("IGK" or "IGL)} \item{from}{string indicating numbering system to convert to ("IMGT" or "KABAT")} \item{to}{string indicating original numbering system ("IMGT" or "KABAT")} \item{calls}{vector of strings representing original numbering} } \value{ A vector of string indicating the corresponding numbering } \description{ Converts numbering systems like Kabat or IMGT using these conventions: http://www.imgt.org/IMGTScientificChart/Numbering/IMGT-Kabat_part1.html with Gaps (unoccupied positions) shown by "G" and Asterisks (*) shown by "S": arbitrary mappings (multiple possible "to" values) represented with "NA" } \examples{ convertNumbering("IGH", "IMGT", "KABAT", c("51", "23", "110")) convertNumbering("IGH", "KABAT", "IMGT", c("51", "23", "G")) } shazam/man/calcBaseline.Rd0000644000176200001440000001327714470664105015165 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{calcBaseline} \alias{calcBaseline} \title{Calculate the BASELINe PDFs (including for regions that include CDR3 and FWR4)} \usage{ calcBaseline( db, sequenceColumn = "clonal_sequence", germlineColumn = "clonal_germline", testStatistic = c("local", "focused", "imbalanced"), regionDefinition = NULL, targetingModel = HH_S5F, mutationDefinition = NULL, calcStats = FALSE, nproc = 1, cloneColumn = NULL, juncLengthColumn = NULL ) } \arguments{ \item{db}{\code{data.frame} containing sequence data and annotations.} \item{sequenceColumn}{\code{character} name of the column in \code{db} containing input sequences.} \item{germlineColumn}{\code{character} name of the column in \code{db} containing germline sequences.} \item{testStatistic}{\code{character} indicating the statistical framework used to test for selection. One of \code{c("local", "focused", "imbalanced")}.} \item{regionDefinition}{\link{RegionDefinition} object defining the regions and boundaries of the Ig sequences.} \item{targetingModel}{\link{TargetingModel} object. Default is \link{HH_S5F}.} \item{mutationDefinition}{\link{MutationDefinition} object defining replacement and silent mutation criteria. If \code{NULL} then replacement and silent are determined by exact amino acid identity. Note, if the input data.frame already contains observed and expected mutation frequency columns then mutations will not be recalculated and this argument will be ignored.} \item{calcStats}{\code{logical} indicating whether or not to calculate the summary statistics \code{data.frame} stored in the \code{stats} slot of a \link{Baseline} object.} \item{nproc}{number of cores to distribute the operation over. If \code{nproc=0} then the \code{cluster} has already been set and will not be reset.} \item{cloneColumn}{\code{character} name of the column in \code{db} containing clonal identifiers. Relevant only for when regionDefinition includes CDR and FWR4 (else this value can be \code{NULL})} \item{juncLengthColumn}{\code{character} name of the column in \code{db} containing the junction length. Relevant only for when regionDefinition includes CDR and FWR4 (else this value can be \code{NULL})} } \value{ A \link{Baseline} object containing the modified \code{db} and BASELINe posterior probability density functions (PDF) for each of the sequences. } \description{ \code{calcBaseline} calculates the BASELINe posterior probability density functions (PDFs) for sequences in the given Change-O \code{data.frame}. } \details{ Calculates the BASELINe posterior probability density function (PDF) for sequences in the provided \code{db}. \strong{Note}: Individual sequences within clonal groups are not, strictly speaking, independent events and it is generally appropriate to only analyze selection pressures on an effective sequence for each clonal group. For this reason, it is strongly recommended that the input \code{db} contains one effective sequence per clone. Effective clonal sequences can be obtained by calling the \link{collapseClones} function. If the \code{db} does not contain the required columns to calculate the PDFs (namely mu_count & mu_expected) then the function will: \enumerate{ \item Calculate the numbers of observed mutations. \item Calculate the expected frequencies of mutations and modify the provided \code{db}. The modified \code{db} will be included as part of the returned \code{Baseline} object. } The \code{testStatistic} indicates the statistical framework used to test for selection. E.g. \itemize{ \item \code{local} = CDR_R / (CDR_R + CDR_S). \item \code{focused} = CDR_R / (CDR_R + CDR_S + FWR_S). \item \code{imbalanced} = CDR_R + CDR_S / (CDR_R + CDR_S + FWR_S + FRW_R). } For \code{focused} the \code{regionDefinition} must only contain two regions. If more than two regions are defined the \code{local} test statistic will be used. For further information on the frame of these tests see Uduman et al. (2011). } \examples{ # Load and subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHG" & sample_id == "+7d") # Collapse clones db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) # Calculate BASELINe baseline <- calcBaseline(db, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc=1) } \references{ \enumerate{ \item Hershberg U, et al. Improved methods for detecting selection by mutation analysis of Ig V region sequences. Int Immunol. 2008 20(5):683-94. \item Uduman M, et al. Detecting selection in immunoglobulin sequences. Nucleic Acids Res. 2011 39(Web Server issue):W499-504. \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ See \link{Baseline} for the return object. See \link{groupBaseline} and \link{summarizeBaseline} for further processing. See \link{plotBaselineSummary} and \link{plotBaselineDensity} for plotting results. } shazam/man/createRegionDefinition.Rd0000644000176200001440000000163714470664105017235 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RegionDefinitions.R \name{createRegionDefinition} \alias{createRegionDefinition} \title{Creates a RegionDefinition} \usage{ createRegionDefinition( name = "", boundaries = factor(), description = "", citation = "" ) } \arguments{ \item{name}{name of the region definition.} \item{boundaries}{\code{factor} defining the region boundaries of the sequence. The levels and values of \code{boundaries} determine the number of regions (e.g. CDR and FWR).} \item{description}{description of the region definition and its source data.} \item{citation}{publication source.} } \value{ A \code{RegionDefinition} object. } \description{ \code{createRegionDefinition} creates a \code{RegionDefinition}. } \examples{ # Creates an empty RegionDefinition object createRegionDefinition() } \seealso{ See \link{RegionDefinition} for the return object. } shazam/man/editBaseline.Rd0000644000176200001440000000250414470664105015177 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Baseline.R \name{editBaseline} \alias{editBaseline} \title{Edit the Baseline object} \usage{ editBaseline(baseline, field, value) } \arguments{ \item{baseline}{\code{Baseline} object to be edited.} \item{field}{name of the field in the \code{Baseline} object to be edited.} \item{value}{value to set the \code{field}.} } \value{ A \code{Baseline} object with the field of choice updated. } \description{ \code{editBaseline} edits a field in a \code{Baseline} object. } \examples{ \donttest{ # Subset example data as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHG" & sample_id == "+7d") set.seed(112) db <- dplyr::slice_sample(db, n=100) # Make Baseline object baseline <- calcBaseline(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", testStatistic="focused", regionDefinition=IMGT_V, targetingModel=HH_S5F, nproc=1) # Edit the field "description" baseline <- editBaseline(baseline, field="description", value="+7d IGHG") } } \seealso{ See \link{Baseline} for the input and return object. } shazam/man/createTargetingMatrix.Rd0000644000176200001440000000550014470664106017104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/TargetingModels.R \name{createTargetingMatrix} \alias{createTargetingMatrix} \title{Calculates a targeting rate matrix} \usage{ createTargetingMatrix(substitutionModel, mutabilityModel) } \arguments{ \item{substitutionModel}{matrix of 5-mers substitution rates built by \link{createSubstitutionMatrix} or \link{extendSubstitutionMatrix}.} \item{mutabilityModel}{vector of 5-mers mutability rates built by \link{createMutabilityMatrix} or \link{extendMutabilityMatrix}.} } \value{ A \code{TargetingMatrix} with the same dimensions as the input \code{substitutionModel} containing normalized targeting probabilities for each 5-mer motif with row names defining the center nucleotide and column names defining the 5-mer nucleotide sequence. If the input \code{mutabilityModel} is of class \code{MutabilityModel}, then the output \code{TargetingMatrix} will carry over the input \code{numMutS} and \code{numMutR} slots. } \description{ \code{createTargetingMatrix} calculates the targeting model matrix as the combined probability of mutability and substitution. } \details{ Targeting rates are calculated by multiplying the normalized mutability rate by the normalized substitution rates for each individual 5-mer. } \examples{ \donttest{ # Subset example data to 50 sequences, of one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:50,] # Create 4x1024 models using only silent mutations sub_model <- createSubstitutionMatrix(db, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") mut_model <- createMutabilityMatrix(db, sub_model, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") # Extend substitution and mutability to including Ns (5x3125 model) sub_model <- extendSubstitutionMatrix(sub_model) mut_model <- extendMutabilityMatrix(mut_model) # Create targeting model from substitution and mutability tar_model <- createTargetingMatrix(sub_model, mut_model) } } \references{ \enumerate{ \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data. Front Immunol. 2013 4(November):358. } } \seealso{ \link{createSubstitutionMatrix}, \link{extendSubstitutionMatrix}, \link{createMutabilityMatrix}, \link{extendMutabilityMatrix}, \link{TargetingMatrix}, \link{createTargetingModel} } shazam/man/shmulateSeq.Rd0000644000176200001440000000451114470664105015102 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/Shmulate.R \name{shmulateSeq} \alias{shmulateSeq} \title{Simulate mutations in a single sequence} \usage{ shmulateSeq( sequence, numMutations, targetingModel = HH_S5F, start = 1, end = nchar(sequence), frequency = FALSE ) } \arguments{ \item{sequence}{sequence string in which mutations are to be introduced. Accepted alphabet: \code{\{A, T, G, C, N, .\}}. Note that \code{-} is not accepted.} \item{numMutations}{a whole number indicating the number of mutations to be introduced into \code{sequence}, if \code{frequency=FALSE}. A fraction bewteen 0 and 1 indicating the mutation frequency if \code{frequency=TRUE}.} \item{targetingModel}{5-mer \link{TargetingModel} object to be used for computing probabilities of mutations at each position. Defaults to \link{HH_S5F}.} \item{start}{Initial position in \code{sequence} where mutations can be introduced. Default: 1} \item{end}{Last position in \code{sequence} where mutations can be introduced. Default: last position (sequence length).} \item{frequency}{If \code{TRUE}, treat \code{numMutations} as a frequency.} } \value{ A string defining the mutated sequence. } \description{ Generates random mutations in a sequence iteratively using a targeting model. Targeting probabilities at each position are updated after each iteration. } \details{ If the input \code{sequence} has a non-triplet overhang at the end, it will be trimmed to the last codon. For example, \code{ATGCATGC} will be trimmed to \code{ATGCAT}. Mutations are not introduced to positions in the input \code{sequence} that contain \code{.} or \code{N}. With \code{frequency=TRUE}, the number of mutations introduced is the \code{floor} of the length of the sequence multiplied by the mutation frequency specified via \code{numMutations}. } \examples{ # Define example input sequence sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATA.TTTA" # Simulate using the default human 5-mer targeting model # Introduce 6 mutations shmulateSeq(sequence, numMutations=6, frequency=FALSE) # Introduction 5\% mutations shmulateSeq(sequence, numMutations=0.05, frequency=TRUE) } \seealso{ See \link{shmulateTree} for imposing mutations on a lineage tree. See \link{HH_S5F} and \link{MK_RS5NF} for predefined \link{TargetingModel} objects. } shazam/man/consensusSequence.Rd0000644000176200001440000001042514470664105016321 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{consensusSequence} \alias{consensusSequence} \title{Construct a consensus sequence} \usage{ consensusSequence( sequences, db = NULL, method = c("mostCommon", "thresholdedFreq", "catchAll", "mostMutated", "leastMutated"), minFreq = NULL, muFreqColumn = NULL, lenLimit = NULL, includeAmbiguous = FALSE, breakTiesStochastic = FALSE, breakTiesByColumns = NULL ) } \arguments{ \item{sequences}{character vector of sequences.} \item{db}{\code{data.frame} containing sequence data for a single clone. Applicable to and required for the \code{"mostMutated"} and \code{"leastMutated"} methods. Default is \code{NULL}.} \item{method}{method to calculate consensus sequence. One of \code{"thresholdedFreq"}, \code{"mostCommon"}, \code{"catchAll"}, \code{"mostMutated"}, or \code{"leastMutated"}. See "Methods" under \link{collapseClones} for details.} \item{minFreq}{frequency threshold for calculating input consensus sequence. Applicable to and required for the \code{"thresholdedFreq"} method. A canonical choice is 0.6. Default is \code{NULL}.} \item{muFreqColumn}{\code{character} name of the column in db containing mutation frequency. Applicable to and required for the \code{"mostMutated"} and \code{"leastMutated"} methods. Default is \code{NULL}.} \item{lenLimit}{limit on consensus length. if \code{NULL} then no length limit is set.} \item{includeAmbiguous}{whether to use ambiguous characters to represent positions at which there are multiple characters with frequencies that are at least \code{minimumFrequency} or that are maximal (i.e. ties). Applicable to and required for the \code{"thresholdedFreq"} and \code{"mostCommon"} methods. Default is \code{FALSE}. See "Choosing ambiguous characters" under \link{collapseClones} for rules on choosing ambiguous characters. Note: this argument refers to the use of ambiguous nucleotides in the output consensus sequence. Ambiguous nucleotides in the input sequences are allowed for methods catchAll, mostMutated and leastMutated.} \item{breakTiesStochastic}{In case of ties, whether to randomly pick a sequence from sequences that fulfill the criteria as consensus. Applicable to and required for all methods except for \code{"catchAll"}. Default is \code{FALSE}. See "Methods" under \link{collapseClones} for details.} \item{breakTiesByColumns}{A list of the form \code{list(c(col_1, col_2, ...), c(fun_1, fun_2, ...))}, where \code{col_i} is a \code{character} name of a column in \code{db}, and \code{fun_i} is a function to be applied on that column. Currently, only \code{max} and \code{min} are supported. Note that the two \code{c()}'s in \code{list()} are essential (i.e. if there is only 1 column, the list should be of the form \code{list(c(col_1), c(func_1))}. Applicable to and optional for the \code{"mostMutated"} and \code{"leastMutated"} methods. If supplied, \code{fun_i}'s are applied on \code{col_i}'s to help break ties. Default is \code{NULL}. See "Methods" under \link{collapseClones} for details.} } \value{ A list containing \code{cons}, which is a character string that is the consensus sequence for \code{sequences}; and \code{muFreq}, which is the maximal/minimal mutation frequency of the consensus sequence for the \code{"mostMutated"} and \code{"leastMutated"} methods, or \code{NULL} for all other methods. } \description{ Construct a consensus sequence } \details{ See \link{collapseClones} for detailed documentation on methods and additional parameters. } \examples{ # Subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHA", "IGHG") & sample_id == "+7d") clone <- subset(db, clone_id == "3192") # First compute mutation frequency for most/leastMutated methods clone <- observedMutations(clone, frequency=TRUE, combine=TRUE) # Manually create a tie clone <- rbind(clone, clone[which.max(clone$mu_freq), ]) # ThresholdedFreq method. # Resolve ties deterministically without using ambiguous characters cons1 <- consensusSequence(clone$sequence_alignment, method="thresholdedFreq", minFreq=0.3, includeAmbiguous=FALSE, breakTiesStochastic=FALSE) cons1$cons } shazam/DESCRIPTION0000644000176200001440000000611314506610332013244 0ustar liggesusersPackage: shazam Type: Package Version: 1.2.0 Date: 2023-10-02 Authors@R: c(person("Mohamed", "Uduman", role=c("aut"), email="mohamed.uduman@yale.edu"), person("Namita", "Gupta", role=c("aut"), email="namita.gupta@yale.edu"), person("Susanna", "Marquez", role=c("aut","cre"), email="susanna.marquez@yale.edu"), person("Julian", "Zhou", role=c("aut"), email="julian.zhou@yale.edu"), person("Nima", "Nouri", role=c("aut"), email="nima.nouri@yale.edu"), person("Noah", "Yann Lee", role=c("aut"), email="noah.yann.lee@yale.edu"), person("Ang", "Cui", role=c("ctb"), email="angcui@mit.edu"), person("Jason", "Vander Heiden", role=c("aut"), email="jason.vanderheiden@gmail.com"), person("Gur", "Yaari", role=c("aut"), email="gur.yaari@biu.ac.il"), person("Steven", "Kleinstein", role=c("aut", "cph"), email="steven.kleinstein@yale.edu")) Title: Immunoglobulin Somatic Hypermutation Analysis Description: Provides a computational framework for analyzing mutations in immunoglobulin (Ig) sequences. Includes methods for Bayesian estimation of antigen-driven selection pressure, mutational load quantification, building of somatic hypermutation (SHM) models, and model-dependent distance calculations. Also includes empirically derived models of SHM for both mice and humans. Citations: Gupta and Vander Heiden, et al (2015) , Yaari, et al (2012) , Yaari, et al (2013) , Cui, et al (2016) . License: AGPL-3 URL: http://shazam.readthedocs.io BugReports: https://bitbucket.org/kleinstein/shazam/issues LazyData: true BuildVignettes: true VignetteBuilder: knitr, rmarkdown Encoding: UTF-8 biocViews: Depends: R (>= 4.0), ggplot2 (>= 3.4.0) Imports: alakazam (>= 1.3.0), ape, diptest, doParallel, dplyr (>= 1.0), foreach, graphics, grid, igraph (>= 1.5.0), iterators, KernSmooth, lazyeval, MASS, methods, parallel, progress, rlang, scales, seqinr, stats, stringi (>= 1.1.3), tidyr, tidyselect, utils Suggests: knitr, rmarkdown, testthat Collate: 'Shazam.R' 'Core.R' 'RegionDefinitions.R' 'Baseline.R' 'ConvertNumbering.R' 'MutationProfiling.R' 'Deprecated.R' 'DistToNearest.R' 'MutationDefinitions.R' 'RegionsExtend.R' 'Shmulate.R' 'TargetingModels.R' 'kedd.R' RoxygenNote: 7.2.3 NeedsCompilation: no Packaged: 2023-10-02 16:57:50 UTC; susanna Author: Mohamed Uduman [aut], Namita Gupta [aut], Susanna Marquez [aut, cre], Julian Zhou [aut], Nima Nouri [aut], Noah Yann Lee [aut], Ang Cui [ctb], Jason Vander Heiden [aut], Gur Yaari [aut], Steven Kleinstein [aut, cph] Maintainer: Susanna Marquez Repository: CRAN Date/Publication: 2023-10-02 18:50:02 UTC shazam/build/0000755000176200001440000000000014506573216012645 5ustar liggesusersshazam/build/vignette.rds0000644000176200001440000000054414506573216015207 0ustar liggesusersSJ0n̉B~~!D_c{dkRonjdܜs>!M Eځ];u8CBҳqx4.Hx1L=10Õ3޻Ϧp7Ѧ$u4<ɦ/kL&@rF^T8'2Js퀫oY Tâ‘tNAP)y1qiXMyO:G=E]FVC'?l۩4t$+u @”$xݲ_q09yPTտܫwE`f?eEYieshazam/build/partial.rdb0000644000176200001440000000007514506573114014771 0ustar liggesusersb```b`afd`b1 H020piּb C"{7shazam/vignettes/0000755000176200001440000000000014506573216013556 5ustar liggesusersshazam/vignettes/Mutation-Vignette.Rmd0000644000176200001440000002307714367147774017631 0ustar liggesusers--- title: 'Shazam: Mutation analysis' author: "Susanna Marquez & Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4.5 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4.5 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Mutation analysis} %\usepackage[utf8]{inputenc} --- Basic mutational load calculations are provided by the `observedMutations` function. `observedMutations` provides multiple options to control how mutations are calculated. Mutations can be calculated as either counts or frequencies, divided into replacement (R) and silent (S) mutations, and subset into FWR and CDR specific mutations. Additionally, alternative mutational definitions may be considered based on the physicochemical properties of translated codons. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. Analyzing mutations requires the following fields (columns) to be present in the table: * `sequence_alignment` * `germline_alignment_d_mask` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(dplyr) library(ggplot2) library(shazam) # Load and subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d") ``` ## Calculate the counts and frequencies of mutations over the entire sequence When calling `observedMutations` with `regionDefinition=NULL`, the entire input sequence (`sequenceColumn`) is compared to the germline sequence (`germlineColumn`) to identify R and S mutations. If `frequency=TRUE`, the number of mutations is expressed as the frequency of mutations over the total number of positions that are non-N in both the input and the germline sequences. In the example below, the counts (`frequency=FALSE` ) and frequencies (`frequency=TRUE`) of R and S mutations are calculated separately. New columns containing mutation counts are appended to the input data.frame with names in the form `mu_count__`. Mutation frequencies appear in new columns named `mu_freq__`. ```{r, eval=TRUE} # Calculate R and S mutation counts db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=FALSE, nproc=1) # Show new mutation count columns db_obs %>% select(sequence_id, starts_with("mu_count_")) %>% head(n=4) # Calculate R and S mutation frequencies db_obs <- observedMutations(db_obs, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=TRUE, nproc=1) # Show new mutation frequency columns db_obs %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ``` Specifying the `combine=TRUE` argument will aggregate all mutation columns into a single value. ```{r, eval=TRUE} # Calculate combined R and S mutation frequencies db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=TRUE, combine=TRUE, nproc=1) # Show new mutation frequency columns db_obs %>% select(sequence_id, starts_with("mu_freq")) %>% head(n=4) ``` We can plot the mutation frequencies and explore differences between samples or isotypes. ```{r, eval=TRUE, warning=FALSE} g1 <- ggplot(db_obs, aes(x=c_call, y=mu_freq, fill=c_call)) + geom_boxplot() + labs(title = "Total mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() plot(g1) ``` ## Calculate mutations within subregions To restrict the mutational analysis to a particular area in the sequence, the `regionDefinition` argument needs to be assigned a `RegionDefinition` object, which simply defines the subregion boundaries of the Ig sequence. For convenience, `shazam` provides a set of such objects, for which an overview is provided via `?IMGT_SCHEMES`. Each of these objects cover the IMGT numbered V segment up to nucleotide position 312. Different objects treat regions within the V segment with varying granularity: * `IMGT_V_BY_CODONS`: treats each codon, from codon 1 to codon 104, as a distinct region; * `IMGT_V_BY_REGIONS`: defines regions to be CDR1, CDR2, FWR1, FWR2 and FWR3; * `IMGT_V`: defines regions to be either CDR or FWR; * `IMGT_V_BY_SEGMENTS`: provides no subdivisions and treats the entire V segment as a single region. * `IMGT_VDJ`: All regions, including CDR3 and FWR4, grouped as either CDR or FWR. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. * `IMGT_VDJ_BY_REGIONS`: CDR1, CDR2, CDR3, FWR1, FWR, FWR3 and FWR4 regions treated as individual regions. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. When supplying one of these objects to `regionDefinition`, and with `combined=FALSE`, the resultant mutation counts/frequencies will be tabulated in a way consistent with the granularity of the object's region definition. For example, * With `IMGT_V_BY_REGIONS`, mutation frequencies will be reported in columns `mu_freq_cdr1_r`, `mu_freq_cdr1_s`, `mu_freq_cdr2_r`, `mu_freq_cdr2_s`, `mu_freq_fwr1_r`, `mu_freq_fwr1_s`, `mu_freq_fwr2_r`, `mu_freq_fwr2_s`, `mu_freq_fwr3_r`, and `mu_freq_fwr3_s`. * With `IMGT_V`, mutation frequencies will be reported in columns `mu_freq_cdr_r`, `mu_freq_cdr_s`, `mu_freq_fwr_r`, and `mu_freq_fwr_s`. * With `IMGT_V_BY_SEGMENTS`, mutation frequencies will be reported in columns `mu_freq_v_r`, and `mu_freq_v_s`. In the following example, we will explore the mutation frequency in the V-segment using two of the region definitions. ```{r, eval=TRUE} # Calculate R and S mutation counts for individual CDRs and FWRs db_obs_v <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V_BY_REGIONS, frequency=FALSE, nproc=1) # Show new FWR mutation columns db_obs_v %>% select(sequence_id, starts_with("mu_count_fwr")) %>% head(n=4) # Calculate aggregate CDR and FWR V-segment R and S mutation frequencies db_obs_v <- observedMutations(db_obs_v, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, frequency=TRUE, nproc=1) # Show new CDR and FWR mutation frequency columns db_obs_v %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ``` Plot a comparison between CDR silent and replacement mutations. ```{r, eval=TRUE, warning=FALSE} g2 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_s, fill=c_call)) + geom_boxplot() + labs(title = "CDR silent mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() g3 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_r, fill=c_call)) + geom_boxplot() + labs(title = "CDR replacement mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() alakazam::gridPlot(g2, g3, ncol=2) ``` ## Use amino acid physicochemical properties to define mutations By default, replacement and silent mutations are determined by exact amino acid identity; this can be changed by setting the `mutationDefinition` argument. For convenience, `shazam` provides a set of `MutationDefinition` objects defining changes in amino acid charge, hydrophobicity, polarity and volume. In the following example, replacement mutations are defined as amino acid changes that lead to a change in charge (`mutationDefinition=CHARGE_MUTATIONS`). Mutations that do not alter the charge classification of a translated codon will be considered silent mutations. ```{r, eval=TRUE} # Calculate charge mutation frequency for the full sequence db_obs_ch <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, mutationDefinition=CHARGE_MUTATIONS, frequency=TRUE, nproc=1) # Show new charge mutation frequency columns db_obs_ch %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ``` We can make a plot to visualize if mutations that change the sequence charge are more frequent in one isotype. ```{r, eval=TRUE, warning=FALSE} g4 <- ggplot(db_obs_ch, aes(x=c_call, y=mu_freq_seq_r, fill=c_call)) + geom_boxplot() + labs(title="Charge replacement mutations", x="Isotype", y="Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() plot(g4) ```shazam/vignettes/Baseline-Vignette.md0000644000176200001440000004144714473147440017416 0ustar liggesusers# 'Shazam: Quantification of selection pressure' BASELINe quantifies selection pressure by calculating the posterior probability density function (PDF) based on observed mutations compared to expected mutation rates derived from an underlying SHM targeting model. Selection is quantified via the following steps: 1. Calculate the selection scores for individual sequences. 2. Group by relevant fields for comparison and convolve individual selection PDFs. 3. Plot and compare selection scores of different groups of sequences. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. The example dataset consists of a subset of Ig sequencing data from an influenza vaccination study (Laserson and Vigneault et al., PNAS, 2014). The data include sequences from multiple time-points before and after the subject received an influenza vaccination. Quantifying selection requires the following fields (columns) to be present in the table: - `sequence_id` - `sequence_alignment` - `germline_alignment_d_mask` ``` r # Import required packages library(alakazam) library(shazam) # Load and subset example data (for faster demonstration) data(ExampleDb, package="alakazam") ExampleDb <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG")) ``` ## Preprocessing Before starting the selection analysis, data need to be prepared in one of two ways: 1. Constructing clonal consensus sequences. 2. Incorporating lineage information. ### Constructing clonal consensus sequences Individual sequences within clonal groups are not, strictly speaking, independent events and it is generally appropriate to only analyze selection pressures on an effective sequence for each clonal group. The `collapseClones` function provides one strategy for generating an effective sequences for each clone. It reduces the input database to one row per clone and appends `clonal_sequence` and `clonal_germline` columns which contain the consensus sequences for each clone. ``` r # Collapse clonal groups into single sequences clones <- collapseClones(ExampleDb, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) ``` ### Incorporating lineage information For each clone, lineage information can be incorporated following these steps: ``` r # Subset to sequences with clone_id=3170 db_3170 <- subset(ExampleDb, clone_id == 3170) dim(db_3170) colnames(db_3170) # Generate a ChangeoClone object for lineage construction clone_3170 <- makeChangeoClone(db_3170, seq="sequence_alignment", germ="germline_alignment") # Run the lineage reconstruction dnapars_exec <- "/usr/local/bin/dnapars" graph_3170 <- buildPhylipLineage(clone_3170, dnapars_exec, rm_temp=TRUE) # Generating a data.frame from the lineage tree graph object, # and merge it with clone data.frame graph_3170_df <- makeGraphDf(graph_3170, clone_3170) dim(graph_3170_df) colnames(graph_3170_df) ``` `makeGraphDf` creates a `data.frame` with the column `parent_sequence`, which can be used to analyze mutations for each sequence relative to their `parent_sequence`. ## Calculate selection PDFs for individual sequences Selection scores are calculated with the `calcBaseline` function. This can be performed with a single call to `calcBaseline`, which performs all required steps. Alternatively, one can perform each step separately for greater control over the analysis parameters. ### Calculating selection in multiple steps Following construction of an effective sequence for each clone, the observed and expected mutation counts are calculated for each sequence in the `clonal_sequence` column relative to the `clonal_germline`. `observedMutations` is used to calculate the number of observed mutations and `expectedMutations` calculates the expected frequency of mutations. The underlying targeting model for calculating expectations can be specified using the `targetingModel` parameter. In the example below, the default `HH_S5F` is used. Column names for sequence and germline sequence may also be passed in as parameters if they differ from the Change-O defaults. Mutations are counted by these functions separately for complementarity determining (CDR) and framework (FWR) regions. The `regionDefinition` argument defines whether these regions are handled separately, and where the boundaries lie. There are several built-in region definitions in the `shazam` package, both dependent upon the V segment being IMGT-gapped: - `IMGT_V`: All regions in the V segment, excluding CDR3, grouped as either CDR or FWR. - `IMGT_V_BY_REGIONS`: The CDR1, CDR2, FWR1, FWR and FWR3 regions in the V segment (no CDR3) treated as individual regions. - `IMGT_VDJ`: All regions, including CDR3 and FWR4, grouped as either CDR or FWR. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. - `IMGT_VDJ_BY_REGIONS`: CDR1, CDR2, CDR3, FWR1, FWR, FWR3 and FWR4 regions treated as individual regions. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. Users may define other region sets and boundaries by creating a custom `RegionDefinition` object. ``` r # Count observed mutations and append mu_count columns to the output observed <- observedMutations(clones, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", regionDefinition=IMGT_V, nproc=1) # Count expected mutations and append mu_exptected columns to the output expected <- expectedMutations(observed, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", targetingModel=HH_S5F, regionDefinition=IMGT_V, nproc=1) ``` The counts of observed and expected mutations can be combined to test for selection using `calcBaseline`. The statistical framework used to test for selection based on mutation counts can be specified using the `testStatistic` parameter. ``` r # Calculate selection scores using the output from expectedMutations baseline <- calcBaseline(expected, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ## calcBaseline will use existing observed and expected mutations, in the fields: mu_count_cdr_r, mu_count_cdr_s, mu_count_fwr_r, mu_count_fwr_s and mu_expected_cdr_r, mu_expected_cdr_s, mu_expected_fwr_r, mu_expected_fwr_s ### Calculating selection in one step It is not required for `observedMutation` and `expectedMutations` to be run prior to `calcBaseline`. If the output of these two steps does not appear in the input data.frame, then `calcBaseline` will call the appropriate functions prior to calculating selection scores. ``` r # Calculate selection scores from scratch baseline <- calcBaseline(clones, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ### Using alternative mutation definitions and models The default behavior of `observedMutations` and `expectedMutations`, and by extension `calcBaseline`, is to define a replacement mutation in the usual way - any change in the amino acid of a codon is considered a replacement mutation. However, these functions have a `mutationDefinition` argument which allows these definitions to be changed by providing a `MutationDefinition` object that contains alternative replacement and silent criteria. `shazam` provides the following built-in `MutationDefinition` objects: - `CHARGE_MUTATIONS`: Amino acid mutations are defined by changes in side chain charge class. - `HYDROPATHY_MUTATIONS`: Amino acid mutations are defined by changes in side chain hydrophobicity class. - `POLARITY_MUTATIONS`: Amino acid mutations are defined by changes in side chain polarity class. - `VOLUME_MUTATIONS`: Amino acid mutations are defined by changes in side chain volume class. The default behavior of `expectedMutations` is to use the human 5-mer mutation model, `HH_S5F`. Alternative SHM targeting models can be provided using the `targetingModel` argument. ``` r # Calculate selection on charge class with the mouse 5-mer model baseline_mk_rs5nf <- calcBaseline(clones, testStatistic="focused", regionDefinition=IMGT_V, targetingModel=MK_RS5NF, mutationDefinition=CHARGE_MUTATIONS, nproc=1) ``` ## Group and convolve individual selection distributions To compare the selection scores of groups of sequences, the sequences must be convolved into a single PDF representing each group. In the example dataset, the `sample_id` field corresponds to samples taken at different time points before and after an influenza vaccination and the `c_call` field specifies the isotype of the sequence. The `groupBaseline` function convolves the BASELINe PDFs of individual sequences/clones to get a combined PDF. The field(s) by which to group the sequences are specified with the `groupBy` parameter. The `groupBaseline` function automatically calls `summarizeBaseline` to generate summary statistics based on the requested groupings, and populates the `stats` slot of the input `Baseline` object with the number of sequences with observed mutations for each region, mean selection scores, 95% confidence intervals, and p-values with positive signs indicating the presence of positive selection and/or p-values with negative signs indicating the presence of negative selection. The magnitudes of the p-values (without the signs) should be interpreted as analogous to a t-test. ### Grouping by a single annotation The following example generates a single selection PDF for each unique annotation in the `sample_id` column. ``` r # Combine selection scores by time-point grouped_1 <- groupBaseline(baseline, groupBy="sample_id") ``` ### Subsetting and grouping by multiple annotations Grouping by multiple annotations follows the sample procedure as a single annotation by simply adding columns to the `groupBy` argument. Subsetting the data can be performed before or after generating selection PDFs via `calcBaseline`. However, note that subsetting may impact the clonal representative sequences generated by `collapseClones`. In the following example, subsetting precedes the collapsing of clonal groups. ``` r # Subset the original data to switched isotypes db_sub <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) # Collapse clonal groups into single sequence clones_sub <- collapseClones(db_sub, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) # Calculate selection scores from scratch baseline_sub <- calcBaseline(clones_sub, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ## calcBaseline will calculate observed and expected mutations for clonal_sequence using clonal_germline as a reference. ``` r # Combine selection scores by time-point and isotype grouped_2 <- groupBaseline(baseline_sub, groupBy=c("sample_id", "c_call")) ``` ### Convolving variables at multiple levels To make selection comparisons using two levels of variables, you would need two iterations of groupings, where the first iteration of `groupBaseline` groups on both variables, and the second iteration groups on the “outer” variable. For example, if a data set has both case and control subjects annotated in `status` and `subject` columns, then generating convolved PDFs for each status would be performed as: ``` r # First group by subject and status subject_grouped <- groupBaseline(baseline, groupBy=c("status", "subject")) # Then group the output by status status_grouped <- groupBaseline(subject_grouped, groupBy="status") ``` ### Testing the difference in selection PDFs between groups The `testBaseline` function will perform significance testing between two grouped BASELINe PDFs, by region, and return a data.frame with the following information: - `region`: The sequence region, such as `cdr` and `fwr`. - `test`: The name of the two groups compared. - `pvalue`: Two-sided p-value for the comparison. - `fdr`: FDR corrected p-value. ``` r testBaseline(grouped_1, groupBy="sample_id") ``` ## region test pvalue fdr ## 1 cdr -1h != +7d 0.04494357 0.08988715 ## 2 fwr -1h != +7d 0.49922881 0.49922881 ## Plot and compare selection scores for groups `plotBaselineSummary` plots the mean and confidence interval of selection scores for the given groups. The `idColumn` argument specifies the field that contains identifiers of the groups of sequences. If there is a secondary field by which the sequences are grouped, this can be specified using the `groupColumn`. This secondary grouping can have a user-defined color palette passed into `groupColors` or can be separated into facets by setting the `facetBy="group"`. The `subsetRegions` argument can be used to visualize selection of specific regions. Several examples utilizing these different parameters are provided below. ``` r # Set sample and isotype colors sample_colors <- c("-1h"="seagreen", "+7d"="steelblue") isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") # Plot mean and confidence interval by time-point plotBaselineSummary(grouped_1, "sample_id") ``` ![](/home/susanna/Documents/Work/Yale/projects/software_projects/shazam/vignettes/Baseline-Vignette_files/figure-gfm/unnamed-chunk-12-1.png) ``` r # Plot selection scores by time-point and isotype for only CDR plotBaselineSummary(grouped_2, "sample_id", "c_call", groupColors=isotype_colors, subsetRegions="cdr") ``` ![](/home/susanna/Documents/Work/Yale/projects/software_projects/shazam/vignettes/Baseline-Vignette_files/figure-gfm/unnamed-chunk-12-2.png) ``` r # Group by CDR/FWR and facet by isotype plotBaselineSummary(grouped_2, "sample_id", "c_call", facetBy="group") ``` ![](/home/susanna/Documents/Work/Yale/projects/software_projects/shazam/vignettes/Baseline-Vignette_files/figure-gfm/unnamed-chunk-12-3.png) `plotBaselineDensity` plots the full `Baseline` PDF of selection scores for the given groups. The parameters are the same as those for `plotBaselineSummary`. However, rather than plotting the mean and confidence interval, the full density function is shown. ``` r # Plot selection PDFs for a subset of the data plotBaselineDensity(grouped_2, "c_call", groupColumn="sample_id", colorElement="group", colorValues=sample_colors, sigmaLimits=c(-1, 1)) ``` ![](/home/susanna/Documents/Work/Yale/projects/software_projects/shazam/vignettes/Baseline-Vignette_files/figure-gfm/unnamed-chunk-13-1.png) ## Editing a field in a Baseline object If for any reason you need to edit the existing values in a field in a `Baseline` object, you can do so via `editBaseline`. In the following example, we remove results related to IGHA in the relevant fields from `grouped_2`. When the input data is large and it takes a long time for `calcBaseline` to run, `editBaseline` could become useful when, for instance, you would like to exclude a certain sample or isotype, but would rather not re-run `calcBaseline` after removing that sample or isotype from the original input data. ``` r # Get indices of rows corresponding to IGHA in the field "db" # These are the same indices also in the matrices in the fileds "numbOfSeqs", # "binomK", "binomN", "binomP", and "pdfs" # In this example, there is one row of IGHA for each sample dbIgMIndex <- which(grouped_2@db[["c_call"]] == "IGHG") grouped_2 <- editBaseline(grouped_2, "db", grouped_2@db[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "numbOfSeqs", grouped_2@numbOfSeqs[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomK", grouped_2@binomK[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomN", grouped_2@binomN[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomP", grouped_2@binomP[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "pdfs", lapply(grouped_2@pdfs, function(pdfs) {pdfs[-dbIgMIndex, ]} )) # The indices corresponding to IGHA are slightly different in the field "stats" # In this example, there is one row of IGHA for each sample and for each region grouped_2 <- editBaseline(grouped_2, "stats", grouped_2@stats[grouped_2@stats[["c_call"]] != "IGHA", ]) ``` shazam/vignettes/Baseline-Vignette.Rmd0000644000176200001440000004140714367147774017550 0ustar liggesusers--- title: 'Shazam: Quantification of selection pressure' author: "Namita Gupta & Jason Anthony Vander Heiden & Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteIndexEntry{Selection quantification} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- BASELINe quantifies selection pressure by calculating the posterior probability density function (PDF) based on observed mutations compared to expected mutation rates derived from an underlying SHM targeting model. Selection is quantified via the following steps: 1. Calculate the selection scores for individual sequences. 2. Group by relevant fields for comparison and convolve individual selection PDFs. 4. Plot and compare selection scores of different groups of sequences. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. The example dataset consists of a subset of Ig sequencing data from an influenza vaccination study (Laserson and Vigneault et al., PNAS, 2014). The data include sequences from multiple time-points before and after the subject received an influenza vaccination. Quantifying selection requires the following fields (columns) to be present in the table: * `sequence_id` * `sequence_alignment` * `germline_alignment_d_mask` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(shazam) # Load and subset example data (for faster demonstration) data(ExampleDb, package="alakazam") ExampleDb <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG")) ``` ## Preprocessing Before starting the selection analysis, data need to be prepared in one of two ways: 1. Constructing clonal consensus sequences. 1. Incorporating lineage information. ### Constructing clonal consensus sequences Individual sequences within clonal groups are not, strictly speaking, independent events and it is generally appropriate to only analyze selection pressures on an effective sequence for each clonal group. The `collapseClones` function provides one strategy for generating an effective sequences for each clone. It reduces the input database to one row per clone and appends `clonal_sequence` and `clonal_germline` columns which contain the consensus sequences for each clone. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Collapse clonal groups into single sequences clones <- collapseClones(ExampleDb, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) ``` ### Incorporating lineage information For each clone, lineage information can be incorporated following these steps: ```{r eval=F, warning=F, results="hide"} # Subset to sequences with clone_id=3170 db_3170 <- subset(ExampleDb, clone_id == 3170) dim(db_3170) colnames(db_3170) # Generate a ChangeoClone object for lineage construction clone_3170 <- makeChangeoClone(db_3170, seq="sequence_alignment", germ="germline_alignment") # Run the lineage reconstruction dnapars_exec <- "/usr/local/bin/dnapars" graph_3170 <- buildPhylipLineage(clone_3170, dnapars_exec, rm_temp=TRUE) # Generating a data.frame from the lineage tree graph object, # and merge it with clone data.frame graph_3170_df <- makeGraphDf(graph_3170, clone_3170) dim(graph_3170_df) colnames(graph_3170_df) ``` `makeGraphDf` creates a `data.frame` with the column `parent_sequence`, which can be used to analyze mutations for each sequence relative to their `parent_sequence`. ## Calculate selection PDFs for individual sequences Selection scores are calculated with the `calcBaseline` function. This can be performed with a single call to `calcBaseline`, which performs all required steps. Alternatively, one can perform each step separately for greater control over the analysis parameters. ### Calculating selection in multiple steps Following construction of an effective sequence for each clone, the observed and expected mutation counts are calculated for each sequence in the `clonal_sequence` column relative to the `clonal_germline`. `observedMutations` is used to calculate the number of observed mutations and `expectedMutations` calculates the expected frequency of mutations. The underlying targeting model for calculating expectations can be specified using the `targetingModel` parameter. In the example below, the default `HH_S5F` is used. Column names for sequence and germline sequence may also be passed in as parameters if they differ from the Change-O defaults. Mutations are counted by these functions separately for complementarity determining (CDR) and framework (FWR) regions. The `regionDefinition` argument defines whether these regions are handled separately, and where the boundaries lie. There are several built-in region definitions in the `shazam` package, both dependent upon the V segment being IMGT-gapped: * `IMGT_V`: All regions in the V segment, excluding CDR3, grouped as either CDR or FWR. * `IMGT_V_BY_REGIONS`: The CDR1, CDR2, FWR1, FWR and FWR3 regions in the V segment (no CDR3) treated as individual regions. * `IMGT_VDJ`: All regions, including CDR3 and FWR4, grouped as either CDR or FWR. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. * `IMGT_VDJ_BY_REGIONS`: CDR1, CDR2, CDR3, FWR1, FWR, FWR3 and FWR4 regions treated as individual regions. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. Users may define other region sets and boundaries by creating a custom `RegionDefinition` object. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Count observed mutations and append mu_count columns to the output observed <- observedMutations(clones, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", regionDefinition=IMGT_V, nproc=1) # Count expected mutations and append mu_exptected columns to the output expected <- expectedMutations(observed, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", targetingModel=HH_S5F, regionDefinition=IMGT_V, nproc=1) ``` The counts of observed and expected mutations can be combined to test for selection using `calcBaseline`. The statistical framework used to test for selection based on mutation counts can be specified using the `testStatistic` parameter. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Calculate selection scores using the output from expectedMutations baseline <- calcBaseline(expected, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ### Calculating selection in one step It is not required for `observedMutation` and `expectedMutations` to be run prior to `calcBaseline`. If the output of these two steps does not appear in the input data.frame, then `calcBaseline` will call the appropriate functions prior to calculating selection scores. ```{r, eval=FALSE, warning=FALSE, results="hide"} # Calculate selection scores from scratch baseline <- calcBaseline(clones, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ### Using alternative mutation definitions and models The default behavior of `observedMutations` and `expectedMutations`, and by extension `calcBaseline`, is to define a replacement mutation in the usual way - any change in the amino acid of a codon is considered a replacement mutation. However, these functions have a `mutationDefinition` argument which allows these definitions to be changed by providing a `MutationDefinition` object that contains alternative replacement and silent criteria. `shazam` provides the following built-in `MutationDefinition` objects: * `CHARGE_MUTATIONS`: Amino acid mutations are defined by changes in side chain charge class. * `HYDROPATHY_MUTATIONS`: Amino acid mutations are defined by changes in side chain hydrophobicity class. * `POLARITY_MUTATIONS`: Amino acid mutations are defined by changes in side chain polarity class. * `VOLUME_MUTATIONS`: Amino acid mutations are defined by changes in side chain volume class. The default behavior of `expectedMutations` is to use the human 5-mer mutation model, `HH_S5F`. Alternative SHM targeting models can be provided using the `targetingModel` argument. ```{r, eval=FALSE, warning=FALSE, results="hide"} # Calculate selection on charge class with the mouse 5-mer model baseline_mk_rs5nf <- calcBaseline(clones, testStatistic="focused", regionDefinition=IMGT_V, targetingModel=MK_RS5NF, mutationDefinition=CHARGE_MUTATIONS, nproc=1) ``` ## Group and convolve individual selection distributions To compare the selection scores of groups of sequences, the sequences must be convolved into a single PDF representing each group. In the example dataset, the `sample_id` field corresponds to samples taken at different time points before and after an influenza vaccination and the `c_call` field specifies the isotype of the sequence. The `groupBaseline` function convolves the BASELINe PDFs of individual sequences/clones to get a combined PDF. The field(s) by which to group the sequences are specified with the `groupBy` parameter. The `groupBaseline` function automatically calls `summarizeBaseline` to generate summary statistics based on the requested groupings, and populates the `stats` slot of the input `Baseline` object with the number of sequences with observed mutations for each region, mean selection scores, 95% confidence intervals, and p-values with positive signs indicating the presence of positive selection and/or p-values with negative signs indicating the presence of negative selection. The magnitudes of the p-values (without the signs) should be interpreted as analogous to a t-test. ### Grouping by a single annotation The following example generates a single selection PDF for each unique annotation in the `sample_id` column. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Combine selection scores by time-point grouped_1 <- groupBaseline(baseline, groupBy="sample_id") ``` ### Subsetting and grouping by multiple annotations Grouping by multiple annotations follows the sample procedure as a single annotation by simply adding columns to the `groupBy` argument. Subsetting the data can be performed before or after generating selection PDFs via `calcBaseline`. However, note that subsetting may impact the clonal representative sequences generated by `collapseClones`. In the following example, subsetting precedes the collapsing of clonal groups. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Subset the original data to switched isotypes db_sub <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) # Collapse clonal groups into single sequence clones_sub <- collapseClones(db_sub, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) # Calculate selection scores from scratch baseline_sub <- calcBaseline(clones_sub, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) # Combine selection scores by time-point and isotype grouped_2 <- groupBaseline(baseline_sub, groupBy=c("sample_id", "c_call")) ``` ### Convolving variables at multiple levels To make selection comparisons using two levels of variables, you would need two iterations of groupings, where the first iteration of `groupBaseline` groups on both variables, and the second iteration groups on the "outer" variable. For example, if a data set has both case and control subjects annotated in `status` and `subject` columns, then generating convolved PDFs for each status would be performed as: ```{r, eval=FALSE, warning=FALSE, results="hide"} # First group by subject and status subject_grouped <- groupBaseline(baseline, groupBy=c("status", "subject")) # Then group the output by status status_grouped <- groupBaseline(subject_grouped, groupBy="status") ``` ### Testing the difference in selection PDFs between groups The `testBaseline` function will perform significance testing between two grouped BASELINe PDFs, by region, and return a data.frame with the following information: * `region`: The sequence region, such as `cdr` and `fwr`. * `test`: The name of the two groups compared. * `pvalue`: Two-sided p-value for the comparison. * `fdr`: FDR corrected p-value. ```{r, eval=TRUE} testBaseline(grouped_1, groupBy="sample_id") ``` ## Plot and compare selection scores for groups `plotBaselineSummary` plots the mean and confidence interval of selection scores for the given groups. The `idColumn` argument specifies the field that contains identifiers of the groups of sequences. If there is a secondary field by which the sequences are grouped, this can be specified using the `groupColumn`. This secondary grouping can have a user-defined color palette passed into `groupColors` or can be separated into facets by setting the `facetBy="group"`. The `subsetRegions` argument can be used to visualize selection of specific regions. Several examples utilizing these different parameters are provided below. ```{r, eval=TRUE, warning=FALSE} # Set sample and isotype colors sample_colors <- c("-1h"="seagreen", "+7d"="steelblue") isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") # Plot mean and confidence interval by time-point plotBaselineSummary(grouped_1, "sample_id") # Plot selection scores by time-point and isotype for only CDR plotBaselineSummary(grouped_2, "sample_id", "c_call", groupColors=isotype_colors, subsetRegions="cdr") # Group by CDR/FWR and facet by isotype plotBaselineSummary(grouped_2, "sample_id", "c_call", facetBy="group") ``` `plotBaselineDensity` plots the full `Baseline` PDF of selection scores for the given groups. The parameters are the same as those for `plotBaselineSummary`. However, rather than plotting the mean and confidence interval, the full density function is shown. ```{r, eval=TRUE, warning=FALSE} # Plot selection PDFs for a subset of the data plotBaselineDensity(grouped_2, "c_call", groupColumn="sample_id", colorElement="group", colorValues=sample_colors, sigmaLimits=c(-1, 1)) ``` ## Editing a field in a Baseline object If for any reason you need to edit the existing values in a field in a `Baseline` object, you can do so via `editBaseline`. In the following example, we remove results related to IGHA in the relevant fields from `grouped_2`. When the input data is large and it takes a long time for `calcBaseline` to run, `editBaseline` could become useful when, for instance, you would like to exclude a certain sample or isotype, but would rather not re-run `calcBaseline` after removing that sample or isotype from the original input data. ```{r, eval=FALSE, warning=FALSE, results="hide"} # Get indices of rows corresponding to IGHA in the field "db" # These are the same indices also in the matrices in the fileds "numbOfSeqs", # "binomK", "binomN", "binomP", and "pdfs" # In this example, there is one row of IGHA for each sample dbIgMIndex <- which(grouped_2@db[["c_call"]] == "IGHG") grouped_2 <- editBaseline(grouped_2, "db", grouped_2@db[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "numbOfSeqs", grouped_2@numbOfSeqs[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomK", grouped_2@binomK[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomN", grouped_2@binomN[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomP", grouped_2@binomP[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "pdfs", lapply(grouped_2@pdfs, function(pdfs) {pdfs[-dbIgMIndex, ]} )) # The indices corresponding to IGHA are slightly different in the field "stats" # In this example, there is one row of IGHA for each sample and for each region grouped_2 <- editBaseline(grouped_2, "stats", grouped_2@stats[grouped_2@stats[["c_call"]] != "IGHA", ]) ``` shazam/vignettes/Targeting-Vignette.Rmd0000644000176200001440000001773514367147774017761 0ustar liggesusers--- title: 'Shazam: Inferring SHM targeting models' author: "Namita Gupta & Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4.5 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4.5 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{SHM targeting models} %\usepackage[utf8]{inputenc} --- The targeting model is the background likelihood of a particular mutation, based on the surrounding sequence context as well as the mutation itself. The model is inferred from observed mutations in the data. The model can then be transformed into a distance function to compare Ig sequences of a given dataset based on the likelihood of the observed mutations. This is done via the following steps: 1. Infer a substitution model, which is the likelihood of a base mutating to each other base given the microsequence context. 2. Infer a mutability model, which is likelihood of a given base being mutated given the microsequence context and substitution model. 3. Visualize the mutability model to identify hot and cold spots. 4. Calculate a nucleotide distance matrix based on the underlying SHM models. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. Inferring a targeting model requires the following fields (columns) to be present in the table: * `sequence_id` * `sequence_alignment` * `germline_alignment_d_mask` * `v_call` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(shazam) # Load example data data(ExampleDb, package="alakazam") # Subset to IGHG for faster usage demonstration db <- subset(ExampleDb, c_call == "IGHG") ``` ## Infer targeting model (substitution and mutability) The function for inferring substitution rates (`createSubstitutionMatrix`) counts the number of mutations from a given base to all others occurring in the center position for all 5-mer motifs in the dataset. The `model` argument of `createSubstitutionMatrix` allows the user to specify whether to count all mutations, or just silent mutations to infer the model. Column names for the sample sequence, germline sequence, and V call can also be passed in as parameters if they differ from Change-O defaults. Additionally, the `multipleMutation` parameter determines handling of multiple mutations in a single 5-mer: `independent` treats each mutation independently and `ignore` entirely disregards 5-mers with multiple mutations. ```{r, eval=FALSE} # Create substitution model using silent mutations sub_model <- createSubstitutionMatrix(db, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") ``` The function for inferring a mutability model (`createMutabilityMatrix`) counts the number of mutations in all 5-mer motifs of the dataset, and depends upon the inferred substitution rates. Similar parameters as those available for inferring the substitution rates are available to adjust this function. ```{r, eval=FALSE} # Create mutability model using silent mutations mut_model <- createMutabilityMatrix(db, sub_model, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") ``` `createMutabilityMatrix` creates an object of class `MutabilityModel` that contains a named numeric vector of 1024 normalized mutability. The numbers of silent and replacement mutations used for estimating the 5-mer mutabilities are recorded in the `numMutS` and `numMutR` slots, respectively. rates. The `source` slot contains a named vector indicating whether each 5-mer mutability was inferred or measured. A data.frame with both the mutability values and derivation source. ```{r, eval=FALSE} # Number of silent mutations used for estimating 5-mer mutabilities mut_model@numMutS # Number of replacement mutations used for estimating 5-mer mutabilities mut_model@numMutR # Mutability and source as a data.frame head(as.data.frame(mut_model)) ``` The inferred substitution and mutability models returned by the above functions only account for unambiguous 5-mers. However, there may be cases in which the user may need the likelihood of a mutation in a 5-mer with ambiguous characters. Each of the above functions has a corresponding function (`extendSubstitutionMatrix` and `extendMutabilityMatrix`) to extend the models to infer 5-mers with Ns by averaging over all corresponding unambiguous 5-mers. ```{r, eval=FALSE} # Extend models to include ambiguous 5-mers sub_model <- extendSubstitutionMatrix(sub_model) mut_model <- extendMutabilityMatrix(mut_model) ``` These extended substitution and mutability models can be used to create an overall SHM targeting matrix (`createTargetingMatrix`), which is the combined probability of mutability and substitution. ```{r, eval=FALSE} # Create targeting model matrix from substitution and mutability models tar_matrix <- createTargetingMatrix(sub_model, mut_model) ``` All of the above steps can be combined by using the single function `createTargetingModel` to infer a `TargetingModel` object directly from the dataset. Again, the numbers of silent and replacement mutations used for estimating the 5-mer mutabilities are also recorded in the `numMutS` and `numMutR` slots respectively. Additionally, it is generally appropriate to consider the mutations within a clone only once. Consensus sequences for each clone can be generated using the `collapseClones` function. ```{r, eval=TRUE, warning=FALSE} # Collapse sequences into clonal consensus clone_db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", nproc=1) # Create targeting model in one step using only silent mutations # Use consensus sequence input and germline columns model <- createTargetingModel(clone_db, model="s", sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", vCallColumn="v_call") ``` ## Visualize targeting model The visualization of a dataset's underlying SHM mutability model can be used to investigate hot and cold spot motifs. The length of the bars on the plot of mutability rates corresponds to the likelihood of a given base in the given 5-mer being mutated. The plotting function `plotMutability` has an argument `style` to specify either a hedgehog plot (circlular) or barplot diplay of 5-mer mutability rates. If the mutability for only specific bases is required, this can be specified via the `nucleotides` argument. ```{r, eval=TRUE, warning=FALSE, fig.width=7, fig.height=7.5} # Generate hedgehog plot of mutability model plotMutability(model, nucleotides="A", style="hedgehog") plotMutability(model, nucleotides="C", style="hedgehog") ``` ```{r, eval=TRUE, warning=FALSE, fig.width=7, fig.height=4.5} # Generate bar plot of mutability model plotMutability(model, nucleotides="G", style="bar") plotMutability(model, nucleotides="T", style="bar") ``` ## Calculate targeting distance matrix In the Change-O pipeline, the `hs5f` cloning method rely on an inferred targeting model. If users wish to use a targeting model inferred from their data to assign distance between sequences for clonal grouping, then the observed SHM targeting rates must be transformed into distances. The `calcTargetingDistance` function returns a matrix of distances between each 5-mer and each corresponding mutation of the center base. This matrix can also be generated and written directly to a file using the function `writeTargetingDistance`. ```{r, eval=TRUE, warning=FALSE} # Calculate distance matrix dist <- calcTargetingDistance(model) ``` shazam/vignettes/Shmulate-Vignette.Rmd0000644000176200001440000001440714367147774017610 0ustar liggesusers--- title: 'Shazam: Simulating sequence mutations' author: "Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 5 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 5 fig_width: 7.5 highlight: pygments theme: readable toc: yes md_document: fig_height: 5 fig_width: 7.5 preserve_yaml: no toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Simulating sequence mutations} %\usepackage[utf8]{inputenc} --- `SHazaM` provides two functions for simulating mutated sequences, one at the sequence level (`shmulateSeq`), and the other at the lineage level (`shmulateTree`). Both functions rely on a 5-mer targeting model for computing the probabilities of mutations at each position along the input sequence. The 5-mer targeting models currently availbale in `SHazaM` are: - `HH_S5F`: Human Heavy chain, Silent, 5-mer, Functional targeting model - `HKL_S5F`: Human Kappa and Lambda light chain, Silent, 5-mer, Functional targeting model - `MK_RS5NF`: Mouse Kappa light chain, Replacement and Silent, 5-mer, Non-Functional targeting model - `U5N`: Uniform 5-mer Null targeting model ## Simulate mutations in a single sequence `shmulateSeq` generates random mutations in an input sequence. This sequence is provided by the user as a string, with the acceptable alphabet being `{A, T, G, C, N, .}`. Note that `-` is not accepted as part of the input sequence. If the input sequence has a non-triplet overhang at the end, it will be trimmed to the last codon. For example, `ATGCATGC` will be trimmed to `ATGCAT` before mutations are introduced. The total number or frequency of mutations to be introduced is user-specified via `numMutations` with `frequency` set to `FALSE` (default) or `TRUE` respectively. For `frequency=TRUE`, the number of mutations to be introduced is calculated as the length of the sequence multiplied by the specified mutation frequency and rounded down to the nearest whole number (`floor`). Mutations are not introduced to positions in the input sequence that contain `.` or `N`. Mutations are introduced iteratively using a targeting model. Targeting probabilities at each position are updated after each iteration. ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(shazam) # Input sequence sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATA.TTTA" # Simulate introduction of 6 mutations using the default HH_S5F targeting model shmulateSeq(sequence, numMutations=6) # Simulate introduction of mutations at frequency 0.2 using the default HH_S5F targeting model shmulateSeq(sequence, numMutations=0.2, frequency=TRUE) # Simulate introduction of 4 mutations using the MK_RS5NF targeting model shmulateSeq(sequence, numMutations=4, targetingModel=MK_RS5NF) ``` ## Simulate mutations in a lineage tree `shmulateTree` generates a set of simulated sequences based on an input sequence and a lineage tree. The input sequence will act as the most recent common ancestor (MRCA) of the lineage tree, and sequences in the offspring nodes will be simulated with the numbers of mutations corresponding to the edge weights of the tree. The lineage tree is supplied by the user as an igraph `graph` object, such as that returned by `buildPhylipLineage` of the `alakazam` package. For details, see the `Reconstruction of Ig lineage trees` vignette of `alakazam`. It is assumed that the `name` vertex attribute of the root node is `Germline`, as is the case with the trees built by `buildPhylipLineage`. ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(igraph) library(shazam) # Load example lineage data(ExampleTrees, package="alakazam") graph <- ExampleTrees[[17]] # Input sequence to be used as MRCA of the lineage tree sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATAGTTTA" # Simulate using the default HH_S5F targeting model shmulateTree(sequence, graph) ``` It is possible to exclude certain specified nodes from being considered as the MRCA and from being included as part of the simulation. To specify such nodes, use the `field` argument to indicate which annotation field in `vertex_attr(graph)` contains information relevant to deciding which nodes to exclude, and the `exclude` argument to indicate the value in the annotation field that nodes to be excluded carry. Note that when excluding some nodes, additional nodes that have not been explicitly specified by the user to be excluded may also get excluded. For example, suppose that node B is an offspring of node A; and node A has been specified by the user to be excluded. As a corollary of node A being excluded, its offspring node B will also become excluded, despite not being specified explicitly. ```{r, eval=TRUE, warning=FALSE} # The annotation field called "sample_id" vertex_attr(graph)$sample_id # notice that node "GN5SHBT01AKANC" is an offspring of "Inferred1" par(mar=c(0, 0, 0, 0) + 0.1) plot(graph, layout=layout_as_tree, edge.arrow.mode=0, vertex.label.cex=0.75) # Exclude nodes without a sample identifier # The nodes "Germline" and "Inferred1" are thus excluded # As a corollary, "GN5SHBT01AKANC", the offspring of "Inferred1", is also excluded # In this case, "GN5SHBT07JDYW5" is then taken to be the MRCA shmulateTree(sequence, graph, field="sample_id", exclude=NA) ``` It is also possible to add a proportional number of mutations to the immediate offsprings of the MRCA based on the fraction of the nucleotide sequence that is within the junction region. This is achieved via the optional `junctionWeight` argument, to be supplied as a numeric value between `0` and `1`. As an example, suppose that the MRCA has two immediate offsprings, each containing 2 and 4 mutations respectively compared to the MRCA. With `junctionWeight=0.2`, the number of mutations to be introduced to these two offsprings will become `round(2*(1+0.2))` (2) and `round(4*(1+0.2))` (5) respectively. ```{r, eval=TRUE, warning=FALSE} # The "Inferred1" node is taken to be the MRCA and has 2 immediate offsprings par(mar=c(0, 0, 0, 0) + 0.1) plot(graph, layout=layout_as_tree, edge.arrow.mode=0, vertex.label.cex=0.75) # Add 20% mutation rate to the immediate offsprings of the MRCA shmulateTree(sequence, graph, junctionWeight=0.2) ``` shazam/vignettes/DistToNearest-Vignette.Rmd0000644000176200001440000004110714367147774020553 0ustar liggesusers--- title: 'Shazam: Tuning clonal assignment thresholds with nearest neighbor distances' author: "Namita Gupta, Susanna Marquez, Nima Nouri and Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteIndexEntry{Distance to nearest neighbor} %\usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- Estimating the optimal distance threshold for partitioning clonally related sequences is accomplished by calculating the distance from each sequence in the data set to its nearest neighbor and finding the break point in the resulting bi-modal distribution that separates clonally related from unrelated sequences. This is done via the following steps: 1. Calculating of the nearest neighbor distances for each sequence. 2. Generating a histogram of the nearest neighbor distances followed by either manual inspect for the threshold separating the two modes or automated threshold detection. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. Calculating the nearest neighbor distances requires the following fields (columns) to be present in the table: * `sequence_id` * `v_call` * `j_call` * `junction` * `junction_length` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(dplyr) library(ggplot2) library(shazam) # Load and subset example data (for speed) data(ExampleDb, package="alakazam") set.seed(112) db <- ExampleDb %>% sample_n(size=500) db %>% count(sample_id) ``` ## Calculating nearest neighbor distances (heavy chain sequences) By default, `distToNearest`, the function for calculating distance between every sequence and its nearest neighbor, assumes that it is running under non-single-cell mode and that every input sequence is a heavy chain sequence and will be used for calculation. It takes a few parameters to adjust how the distance is measured. * If a genotype has been inferred using the methods in the `tigger` package, and a `v_call_genotyped` field has been added to the database, then this column may be used instead of the default `v_call` column by specifying the `vCallColumn` argument. * This will allows the more accurate V call from `tigger` to be used for grouping of the sequences. * Furthermore, for more leniency toward ambiguous V(D)J segment calls, the parameter `first` can be set to `FALSE`. * Setting `first=FALSE` will use the union of all possible genes to group sequences, rather than the first gene in the field. * The `model` parameter determines which underlying SHM model is used to calculate the distance. * The default model is single nucleotide Hamming distance with gaps considered as a match to any nucleotide (`ham`). * Other options include a human Ig-specific single nucleotide model similar to a transition/transversion model (`hh_s1f`) and the corresponding 5-mer context model from Yaari et al, 2013 (`hh_s5f`), an analogous pair of mouse specific models from Cui et al, 2016 (`mk_rs1nf` and `mk_rs5nf`), and amino acid Hamming distance (`aa`). **Note:** Human and mouse distance measures that are backward compatible with SHazaM v0.1.4 and Change-O v0.3.3 are also provided as `hs1f_compat` and `m1n_compat`, respectively. For models that are not symmetric (e.g., distance from A to B is not equal to the distance from B to A), there is a `symmetry` parameter that allows the user to specify whether the average or minimum of the two distances is used to determine the overall distance. ```{r, eval=TRUE, warning=FALSE} # Use nucleotide Hamming distance and normalize by junction length dist_ham <- distToNearest(db %>% filter(sample_id == "+7d"), sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # Use genotyped V assignments, a 5-mer model and no normalization dist_s5f <- distToNearest(db %>% filter(sample_id == "+7d"), sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="hh_s5f", normalize="none", nproc=1) ``` ## Calculating nearest neighbor distances (single-cell paired heavy and light chain sequences) The `distToNearest` function also supports running under single-cell mode where an input `Example10x` containing single-cell paired IGH:IGK/IGL, TRB:TRA, or TRD:TRG chain sequences are supplied. In this case, by default, cells are first divided into partitions containing the same heavy/long chain (IGH, TRB, TRD) V gene and J gene (and if specified, junction length), and the same light/short chain (IGK, IGL, TRA, TRG) V gene and J gene (and if specified, junction length). Then, only the heavy chain sequences are used for calculating the nearest neighbor distances. Under the single-cell mode, each row of the input `Example10x` should represent a sequence/chain. Sequences/chains from the same cell are linked by a cell ID in a `cellIdColumn` column. Note that a cell should have exactly one `IGH` sequence (BCR) or `TRB`/`TRD` (TCR). The values in the `locusColumn` column must be one of `IGH`, `IGI`, `IGK`, or `IGL` (BCR) or `TRA`, `TRB`, `TRD`, or `TRG` (TCR). To invoke the single-cell mode, `cellIdColumn` must be specified and `locusColumn` must be correct. There is a choice of whether grouping should be done as a one-stage process or a two-stage process. This can be specified via `VJthenLen`. * In the one-stage process (`VJthenLen=FALSE`), cells are divided into partitions containing same heavy/long chain V gene, J gene, and junction length (V-J-length combination), and the same light chain V-J-length combination. * In the two-stage process (`VJthenLen=TRUE`), cells are first divided by heavy/long chain V gene and J gene (V-J combination), and light/short chain V-J combination; and then by the corresponding junction lengths. There is also a choice of whether grouping should be done using `IGH` (BCR) or `TRB/TRD` (TCR) sequences only, or using both `IGH` and `IGK`/`IGL` (BCR) or `TRB`/`TRD` and `TRA`/`TRG` (TCR) sequences. This is governed by `onlyHeavy`. ```{r, eval=FALSE, warning=FALSE} # Single-cell mode # Group cells in a one-stage process (VJthenLen=FALSE) and using # both heavy and light chain sequences (onlyHeavy=FALSE) data(Example10x, package="alakazam") dist_sc <- distToNearest(Example10x, cellIdColumn="cell_id", locusColumn="locus", VJthenLen=FALSE, onlyHeavy=FALSE) ``` Regardless of whether grouping was done using only the heavy chain sequences, or both heavy and light chain sequences, only heavy chain sequences will be used for calculating the nearest neighbor distances. Hence, under the single-cell mode, rows in the returned `data.frame` corresponding to light chain sequences will have `NA` in the `dist_nearest` field. ## Using nearest neighbor distances to determine clonal assignment thresholds The primary use of the distance to nearest calculation in SHazaM is to determine the optimal threshold for clonal assignment using the `DefineClones` tool in Change-O. Defining a threshold relies on distinguishing clonally related sequences (represented by sequences with close neighbors) from singletons (sequences without close neighbors), which show up as two modes in a nearest neighbor distance histogram. Thresholds may be manually determined by inspection of the nearest neighbor histograms or by using one of the automated threshold detection algorithms provided by the `findThreshold` function. The available methods are `density` (smoothed density) and `gmm` (gamma/Gaussian mixture model), and are chosen via the `method` parameter of `findThreshold`. ### Threshold determination by manual inspection Manual threshold detection simply involves generating a histrogram for the values in the `dist_nearest` column of the `distToNearest` output and selecting a suitable value within the valley between the two modes. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate Hamming distance histogram p1 <- ggplot(subset(dist_ham, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + labs(x = "Hamming distance", y = "Count") + scale_x_continuous(breaks=seq(0, 1, 0.1)) + theme_bw() plot(p1) ``` By manual inspection, the length normalized `ham` model distance threshold would be set to a value near 0.12 in the above example. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate HH_S5F distance histogram p2 <- ggplot(subset(dist_s5f, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=1) + geom_vline(xintercept=7, color="firebrick", linetype=2) + labs(x = "HH_S5F distance", y = "Count") + scale_x_continuous(breaks=seq(0, 50, 5)) + theme_bw() plot(p2) ``` In this example, the unnormalized `hh_s5f` model distance threshold would be set to a value near 7. ### Automated threshold detection via smoothed density The `density` method will look for the minimum in the valley between two modes of a smoothed distribution based on the input vector (`distances`), which will generally be the `dist_nearest` column from the `distToNearest` output. Below is an example of using the `density` method for threshold detection. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Find threshold using density method output <- findThreshold(dist_ham$dist_nearest, method="density") threshold <- output@threshold # Plot distance histogram, density estimate and optimum threshold plot(output, title="Density Method") # Print threshold print(output) ``` ### Automated threshold detection via a mixture model The `findThreshold` function includes approaches for automatically determining a clonal assignment threshold. The `"gmm"` method (gamma/Gaussian mixture method) of `findThreshold` (`method="gmm"`) performs a maximum-likelihood fitting procedure over the distance-to-nearest distribution using one of four combinations of univariate density distribution functions: `"norm-norm"` (two Gaussian distributions), `"norm-gamma"` (lower Gaussian and upper gamma distribution), `"gamma-norm"` (lower gamm and upper Gaussian distribution), and `"gamma-gamma"` (two gamma distributions). By default, the threshold will be selected by calculating the distance at which the average of sensitivity and specificity reaches its maximum (`cutoff="optimal"`). Alternative threshold selection criteria are also providing, including the curve intersection (`cutoff="intersect"`), user defined sensitivity (`cutoff="user", sen=x`), or user defined specificity (`cutoff="user", spc=x`) In the example below the mixture model method (`method="gmm"`) is used to find the optimal threshold for separating clonally related sequences by fitting two gamma distributions (`model="gamma-gamma"`). The red dashed-line shown in figure below defines the distance where the average of the sensitivity and specificity reaches its maximum. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Find threshold using gmm method output <- findThreshold(dist_ham$dist_nearest, method="gmm", model="gamma-gamma") # Plot distance histogram, Gaussian fits, and optimum threshold plot(output, binwidth=0.02, title="GMM Method: gamma-gamma") # Print threshold print(output) ``` **Note:** The shape of histogram plotted by `plotGmmThreshold` is governed by the `binwidth` parameter. Meaning, any change in bin size will change the form of the distribution, while the `gmm` method is completely bin size independent and only engages the real input data. ## Calculating nearest neighbor distances independently for subsets of data The `fields` argument to `distToNearest` will split the input `data.frame` into groups based on values in the specified fields (columns) and will treat them independently. For example, if the input data has multiple samples, then `fields="sample_id"` would allow each sample to be analyzed separately. In the previous examples we used a subset of the original example data. In the following example, we will use the two available samples, `-1h` and `+7d`, and will set `fields="sample_id"`. This will reproduce previous results for sample `+7d` and add results for sample `-1d`. ```{r fields, eval=TRUE, warning=FALSE} dist_fields <- distToNearest(db, model="ham", normalize="len", fields="sample_id", nproc=1) ``` We can plot the nearest neighbor distances for the two samples: ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate grouped histograms p4 <- ggplot(subset(dist_fields, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + labs(x = "Grouped Hamming distance", y = "Count") + facet_grid(sample_id ~ ., scales="free_y") + theme_bw() plot(p4) ``` In this case, the threshold selected for `+7d` seems to work well for `-1d` as well. ## Calculating nearest neighbor distances across groups rather than within a groups Specifying the `cross` argument to `distToNearest` forces distance calculations to be performed across groups, such that the nearest neighbor of each sequence will always be a sequence in a different group. In the following example we set `cross="sample"`, which will group the data into `-1h` and `+7d` sample subsets. Thus, nearest neighbor distances for sequences in sample `-1h` will be restricted to the closest sequence in sample `+7d` and vice versa. ```{r cross, eval=TRUE, warning=FALSE} dist_cross <- distToNearest(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, normalize="len", cross="sample_id", nproc=1) ``` ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate cross sample histograms p5 <- ggplot(subset(dist_cross, !is.na(cross_dist_nearest)), aes(x=cross_dist_nearest)) + labs(x = "Cross-sample Hamming distance", y = "Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") + theme_bw() plot(p5) ``` This can provide a sense of overlap between samples or a way to compare within-sample variation to cross-sample variation. ## Speeding up pairwise-distance-matrix calculations with subsampling The `subsample` option in `distToNearest` allows to speed up calculations and reduce memory usage. If there are very large groups of sequences that share V call, J call and junction length, `distToNearest` will need a lot of memory and it will take a long time to calculate all the distances. Without subsampling, in a large group of n=70,000 sequences `distToNearest` calculates a n\*n distance matrix. With subsampling, e.g. to s=15,000, the distance matrix for the same group has size s\*n, and for each sequence in `db`, the distance value is calculated by comparing the sequence to the subsampled sequences from the same V-J-junction length group. ```{r subsample, eval=TRUE, warning=FALSE} # Explore V-J-junction length groups sizes to use subsample # Show the size of the largest groups top_10_sizes <- ExampleDb %>% group_by(junction_length) %>% # Group by junction length do(alakazam::groupGenes(., first=TRUE)) %>% # Group by V and J call mutate(GROUP_ID=paste(junction_length, vj_group, sep="_")) %>% # Create group ids ungroup() %>% group_by(GROUP_ID) %>% # Group by GROUP_ID distinct(junction) %>% # Count unique junctions per group summarize(SIZE=n()) %>% # Get the size of the group arrange(desc(SIZE)) %>% # Sort by decreasing size select(SIZE) %>% top_n(10) # Filter to the top 10 top_10_sizes # Use 30 to subsample # NOTE: This is a toy example. Subsampling to 30 sequence with real data is unwise dist <- distToNearest(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, normalize="len", subsample=30) ``` shazam/R/0000755000176200001440000000000014506551517011747 5ustar liggesusersshazam/R/MutationDefinitions.R0000644000176200001440000001706314477634424016103 0ustar liggesusers# Class definitions for mutation classes #' @include Shazam.R #' @include Core.R NULL #### Classes #### #' S4 class defining replacement and silent mutation definitions #' #' \code{MutationDefinition} defines a common data structure for defining the whether #' a mutation is annotated as a replacement or silent mutation. #' #' @slot name name of the MutationDefinition. #' @slot description description of the model and its source. #' @slot classes named character vectors with single-letter amino acid codes as names #' and amino acid classes as values, with \code{NA} assigned to set of #' characters \code{c("X", "*", "-", ".")}. Replacement (R) is be #' defined as a change in amino acid class and silent (S) as no #' change in class. #' @slot codonTable matrix of codons (columns) and substitutions (rows). #' @slot citation publication source. #' #' @seealso #' See \link{MUTATION_SCHEMES} for a set of predefined \code{MutationDefinition} objects. #' #' @name MutationDefinition-class #' @rdname MutationDefinition-class #' @aliases MutationDefinition #' @exportClass MutationDefinition setClass("MutationDefinition", slots=c(name="character", description="character", classes="character", codonTable="matrix", citation="character")) #### Builder functions #### # Create all codons one mutation away from input codon. # # All codons one mutation away from the input codon are generated. # # @param codon starting codon to which mutations are added # @return a vector of codons. allCodonMuts <- function(codon) { codon_char <- seqinr::s2c(codon) matCodons <- t(array(codon_char, dim=c(3,12))) matCodons[1:4, 1] <- NUCLEOTIDES[1:4] matCodons[5:8, 2] <- NUCLEOTIDES[1:4] matCodons[9:12,3] <- NUCLEOTIDES[1:4] return(apply(matCodons, 1, seqinr::c2s)) } # Generate codon table # # First generates all informative codons and determines types of mutations. # Next generates uninformative codons (having either an N or a gap "-" # character) and sets the mutation type as NA. # # @param aminoAcidClasses vector of amino acid trait classes # if NULL then R or S is determined by amino acid identity # @return matrix with all codons as row and column names and the type of mutation as # the corresponding value in the matrix. # @examples # library(alakazam) # hydropathy <- list(hydrophobic=c("A", "I", "L", "M", "F", "W", "V"), # hydrophilic=c("R", "N", "D", "C", "Q", "E", "K"), # neutral=c("G", "H", "P", "S", "T", "Y")) # chars <- unlist(hydropathy, use.names=FALSE) # classes <- setNames(translateStrings(chars, hydropathy), chars) # computeCodonTable(aminoAcidClasses=classes) computeCodonTable <- function(aminoAcidClasses=NULL) { # Initialize empty data.frame codon_table <- as.data.frame(matrix(NA, ncol=64, nrow=12)) # Pre-compute every codon counter <- 1 for(pOne in NUCLEOTIDES[1:4]) { for(pTwo in NUCLEOTIDES[1:4]) { for(pThree in NUCLEOTIDES[1:4]) { codon <- paste0(pOne, pTwo, pThree) colnames(codon_table)[counter] <- codon counter <- counter + 1 all_muts <- allCodonMuts(codon) codon_table[, codon] <- sapply(all_muts, function(x) { mutType = mutationType(x, codon, aminoAcidClasses=aminoAcidClasses) mutType = names(mutType)[which(mutType>0)] # does not support ambiguous characters # assumes that only 1 entry (r/s/stop/na) from mutationType is non-zero/1 stopifnot(length(mutType)==1) if (mutType=="na") {mutType=NA} return(mutType) }) } } } # Set codons with N or . to be NA chars <- c("N","A","C","G","T", ".") for(n1 in chars) { for(n2 in chars) { for(n3 in chars) { if(n1=="N" | n2=="N" | n3=="N" | n1=="." | n2=="." | n3==".") { codon_table[, paste0(n1, n2, n3)] <- rep(NA, 12) } } } } return(as.matrix(codon_table)) } #' Creates a MutationDefinition #' #' \code{createMutationDefinition} creates a \code{MutationDefinition}. #' #' @param name name of the mutation definition. #' @param classes named character vectors with single-letter amino acid codes as names #' and amino acid classes as values, with \code{NA} assigned to set of #' characters \code{c("X", "*", "-", ".")}. Replacement (R) is be #' defined as a change in amino acid class and silent (S) as no #' change in class. #' @param description description of the mutation definition and its source data. #' @param citation publication source. #' #' @return A \code{MutationDefinition} object. #' #' @seealso See \link{MutationDefinition} for the return object. #' #' @examples #' # Define hydropathy classes #' suppressPackageStartupMessages(library(alakazam)) #' hydropathy <- list(hydrophobic=c("A", "I", "L", "M", "F", "W", "V"), #' hydrophilic=c("R", "N", "D", "C", "Q", "E", "K"), #' neutral=c("G", "H", "P", "S", "T", "Y")) #' chars <- unlist(hydropathy, use.names=FALSE) #' classes <- setNames(translateStrings(chars, hydropathy), chars) #' #' # Create hydropathy mutation definition #' md <- createMutationDefinition("Hydropathy", classes) #' #' @export createMutationDefinition <- function(name, classes, description="", citation="") { # Build the codon table codonTable <- computeCodonTable(aminoAcidClasses=classes) # Define MutationDefinition object md <- new("MutationDefinition", name=name, description=description, classes=classes, codonTable=codonTable, citation=citation) return(md) } #### Data #### #' Amino acid mutation definitions #' #' Definitions of replacement (R) and silent (S) mutations for different amino acid #' physicochemical classes. #' #' @format A \link{MutationDefinition} object defining: #' \itemize{ #' \item \code{CHARGE_MUTATIONS}: Amino acid mutations are defined by changes #' in side chain charge class. #' \item \code{HYDROPATHY_MUTATIONS}: Amino acid mutations are defined by changes #' in side chain hydrophobicity class. #' \item \code{POLARITY_MUTATIONS}: Amino acid mutations are defined by changes #' in side chain polarity class. #' \item \code{VOLUME_MUTATIONS}: Amino acid mutations are defined by changes #' in side chain volume class. #' } #' #' @references #' \enumerate{ #' \item \url{https://www.imgt.org/IMGTeducation/Aide-memoire/_UK/aminoacids/IMGTclasses.html} #' } #' #' @name MUTATION_SCHEMES NULL #' @name CHARGE_MUTATIONS #' @rdname MUTATION_SCHEMES NULL #' @name HYDROPATHY_MUTATIONS #' @rdname MUTATION_SCHEMES NULL #' @name POLARITY_MUTATIONS #' @rdname MUTATION_SCHEMES NULL #' @name VOLUME_MUTATIONS #' @rdname MUTATION_SCHEMES NULL shazam/R/Baseline.R0000644000176200001440000030011314502041714013577 0ustar liggesusers# Selection analysis using BASELINe #' @include RegionDefinitions.R #' @include Shazam.R NULL #### Classes #### #' S4 class defining a BASELINe (selection) object #' #' \code{Baseline} defines a common data structure the results of selection #' analysis using the BASELINe method. #' #' @slot description \code{character} providing general information regarding the #' sequences, selection analysis and/or object. #' @slot db \code{data.frame} containing annotation information about #' the sequences and selection results. #' @slot regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. #' @slot testStatistic \code{character} indicating the statistical framework #' used to test for selection. For example, \code{"local"} or #' \code{"focused"}. #' @slot regions \code{character} vector defining the regions the BASELINe #' analysis was carried out on. For \code{"cdr"} and \code{"fwr"} #' or \code{"cdr1"}, \code{"cdr2"}, \code{"cdr3"}, etc. #' @slot numbOfSeqs \code{matrix} of dimensions \code{r x c} containing the number of #' sequences or PDFs in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @slot binomK \code{matrix} of dimensions \code{r x c} containing the number of #' successes in the binomial trials in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @slot binomN \code{matrix} of dimensions \code{r x c} containing the total #' number of trials in the binomial in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @slot binomP \code{matrix} of dimensions \code{r x c} containing the probability #' of success in one binomial trial in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @slot pdfs \code{list} of matrices containing PDFs with one item for each #' defined region (e.g. \code{cdr} and \code{fwr}). Matrices have dimensions #' \code{r x c} dementions, where:\cr #' \code{r} = number of rows = number of sequences or groups. \cr #' \code{c} = number of columns = length of the PDF (default 4001). #' @slot stats \code{data.frame} of BASELINe statistics, #' including: mean selection strength (mean Sigma), 95\% confidence #' intervals, and p-values with positive signs for the presence of #' positive selection and/or p-values with negative signs for the #' presence of negative selection. #' #' @name Baseline-class #' @rdname Baseline-class #' @aliases Baseline #' @exportClass Baseline #' @seealso See \link{summarizeBaseline} for more information on \code{@stats}. setClass("Baseline", slots=c(description="character", db="data.frame", regionDefinition="RegionDefinition", testStatistic="character", regions="character", numbOfSeqs="matrix", binomK="matrix", binomN="matrix", binomP="matrix", pdfs="list", stats="data.frame")) #### Methods ##### #' @param x \code{Baseline} object. #' @param y name of the column in the \code{db} slot of \code{baseline} #' containing primary identifiers. #' @param ... arguments to pass to \link{plotBaselineDensity}. #' #' @rdname Baseline-class #' @aliases Baseline-method #' @export setMethod("plot", c(x="Baseline", y="character"), function(x, y, ...) { plotBaselineDensity(x, y, ...) }) #' @param object \code{Baseline} object. #' @param nproc number of cores to distribute the operation over. #' #' @rdname Baseline-class #' @aliases Baseline-method #' @export setMethod("summary", c(object="Baseline", nproc=integer()), function(object, nproc=1) { summarizeBaseline(object, returnType="df", nproc=nproc) }) #### Accessory functions ##### #' Creates a Baseline object #' #' \code{createBaseline} creates and initialize a \code{Baseline} object. #' #' @param description \code{character} providing general information regarding the #' sequences, selection analysis and/or object. #' @param db \code{data.frame} containing annotation information about #' the sequences and selection results. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. #' @param testStatistic \code{character} indicating the statistical framework #' used to test for selection. For example, \code{"local"} or #' \code{"focused"} or \code{"imbalanced"}. #' @param regions \code{character} vector defining the regions the BASELINe #' analysis was carried out on. For \code{"cdr"} and \code{"fwr"} #' or \code{"cdr1"}, \code{"cdr2"}, \code{"cdr3"}, etc. If \code{NULL} #' then regions will be determined automatically from \code{regionDefinition}. #' @param numbOfSeqs \code{matrix} of dimensions \code{r x c} containing the number of #' sequences or PDFs in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @param binomK \code{matrix} of dimensions \code{r x c} containing the number of #' successes in the binomial trials in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @param binomN \code{matrix} of dimensions \code{r x c} containing the total #' number of trials in the binomial in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @param binomP \code{matrix} of dimensions \code{r x c} containing the probability #' of success in one binomial trial in each region, where:\cr #' \code{r} = number of rows = number of groups or sequences.\cr #' \code{c} = number of columns = number of regions. #' @param pdfs \code{list} of matrices containing PDFs with one item for each #' defined region (e.g. \code{cdr} and \code{fwr}). Matrices have dimensions #' \code{r x c} dementions, where:\cr #' \code{r} = number of rows = number of sequences or groups. \cr #' \code{c} = number of columns = length of the PDF (default 4001). #' @param stats \code{data.frame} of BASELINe statistics, #' including: mean selection strength (mean Sigma), 95\% confidence #' intervals, and p-values with positive signs for the presence of #' positive selection and/or p-values with negative signs for the #' presence of negative selection. #' #' @return A \code{Baseline} object. #' #' @details #' Create and initialize a \code{Baseline} object. #' #' The \code{testStatistic} indicates the statistical framework used to test for selection. #' For example, #' \itemize{ #' \item \code{local} = CDR_R / (CDR_R + CDR_S). #' \item \code{focused} = CDR_R / (CDR_R + CDR_S + FWR_S). #' \item \code{immbalance} = CDR_R + CDR_s / (CDR_R + CDR_S + FWR_S + FWR_R) #' } #' For \code{focused} the \code{regionDefinition} must only contain two regions. If more #' than two regions are defined, then the \code{local} test statistic will be used. #' For further information on the frame of these tests see Uduman et al. (2011). #' #' @seealso See \link{Baseline} for the return object. #' #' @references #' \enumerate{ #' \item Hershberg U, et al. Improved methods for detecting selection by mutation #' analysis of Ig V region sequences. #' Int Immunol. 2008 20(5):683-94. #' \item Uduman M, et al. Detecting selection in immunoglobulin sequences. #' Nucleic Acids Res. 2011 39(Web Server issue):W499-504. #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @examples #' # Creates an empty Baseline object #' createBaseline() #' #' @export createBaseline <- function(description="", db=data.frame(), regionDefinition=createRegionDefinition(), testStatistic="", regions=NULL, numbOfSeqs=matrix(), binomK=matrix(), binomN=matrix(), binomP=matrix(), pdfs=list(), stats=data.frame()) { if (is.null(regionDefinition)) { regionDefinition <- makeNullRegionDefinition() } # Get regions if not passing in if (is.null(regions)) { regions <- regionDefinition@regions } # Define empty stats data.frame if not passed in if (nrow(stats) == 0) { stats <- data.frame(group=character(), region=character(), baseline_sigma=character(), baseline_ci_lower=character(), baseline_ci_upper=character(), baseline_ci_pvalue=character(), stringsAsFactors=FALSE) } # Define RegionDefinition object baseline <- new("Baseline", description=description, db=as.data.frame(db), regionDefinition=regionDefinition, testStatistic=testStatistic, regions=regionDefinition@regions, numbOfSeqs=numbOfSeqs, binomK=binomK, binomN=binomN, binomP=binomP, pdfs=pdfs, stats=as.data.frame(stats)) return(baseline) } #' Edit the Baseline object #' #' \code{editBaseline} edits a field in a \code{Baseline} object. #' #' @param baseline \code{Baseline} object to be edited. #' @param field name of the field in the \code{Baseline} object to be edited. #' @param value value to set the \code{field}. #' #' @return A \code{Baseline} object with the field of choice updated. #' #' @seealso See \link{Baseline} for the input and return object. #' #' @examples #' \donttest{ #' # Subset example data as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHG" & sample_id == "+7d") #' set.seed(112) #' db <- dplyr::slice_sample(db, n=100) #' #' # Make Baseline object #' baseline <- calcBaseline(db, #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc=1) #' #' # Edit the field "description" #' baseline <- editBaseline(baseline, field="description", #' value="+7d IGHG") #' } #' #' @export editBaseline <- function(baseline, field, value) { if (!match(field, slotNames(baseline))) { stop(field, " is not part of the Baseline object.") } slot(baseline, field) <- value return(baseline) } #### Calculation functions #### # Helper function for calcBaseline # # @param observed # @param expected # @param region # @param testStatistic # @param regionDefinition # # @return A modified \link{Baseline} object with the BASELINe probability # density function calculated for the regions defined in the \code{regionDefinition}. calcBaselineHelper <- function(observed, expected, region, testStatistic="local", regionDefinition=NULL) { # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } if (is.null(regionDefinition)) { regions <- makeNullRegionDefinition()@regions } else { regions <- regionDefinition@regions } # Evaluate argument choices testStatistic <- match.arg(testStatistic, c("local", "focused", "imbalanced")) # If there are more than two regions (e.g. CDR and FWR then you cannot perform the focused test) if (testStatistic=="focused" & length(regions)!=2) { testStatistic="local" } # local test statistic if (testStatistic == "local") { obsX_Index <- grep( paste0("mu_count_", region,"_r"), names(observed) ) # important to have "_" after region # otherwise this might happen (leading to bugs in results): # region = codon_1 # expect grep to find only codon_1_S and codon_1_R # in fact, however, codon_10_S, codon_10_R, codon_101_S, codon_101_R are matched obsN_Index <- grep( paste0("mu_count_", region, "_"), names(observed) ) expX_Index <- grep( paste0("mu_expected_", region,"_r"), names(expected) ) # important to have "_" after region expN_Index <- grep( paste0("mu_expected_", region, "_"), names(expected) ) } # focused test statistic if (testStatistic == "focused") { obsX_Index <- grep( paste0("mu_count_", region,"_r"), names(observed) ) obsN_Index <- grep( paste0( "mu_count_", region, "|", "mu_count_", regions[regions!=region], "_s" ), names(observed) ) expX_Index <- grep( paste0("mu_expected_", region,"_r"), names(expected) ) expN_Index <- grep( paste0( "mu_expected_", region, "|", "mu_expected_", regions[regions!=region], "_s" ), names(expected) ) } # imbalanced test statistic if (testStatistic == "imbalanced") { obsX_Index <- grep( paste0("mu_count_", region), names(observed) ) obsN_Index <- grep( "mu_count_",names(observed)) expX_Index <- grep( paste0("mu_expected_", region), names(expected) ) expN_Index <- grep( "mu_expected_",names(expected)) } obsX <- sum(as.numeric( observed[obsX_Index] )) obsN <- sum(as.numeric(observed[obsN_Index]), na.rm=T ) expP <- as.numeric( sum(expected[expX_Index]) / sum( expected[expN_Index], na.rm=T ) ) return( c( calcBaselineBinomialPdf( x=obsX, n=obsN, p=expP ), obsX, obsN, expP ) ) } # Calculate the BASELINe probability function in a binomial framework. calcBaselineBinomialPdf <- function (x=3, n=10, p=0.33, CONST_i=CONST_I, max_sigma=20, length_sigma=4001) { if(n!=0){ sigma_s<-seq(-max_sigma,max_sigma,length.out=length_sigma) sigma_1<-log({CONST_i/{1-CONST_i}}/{p/{1-p}}) index<-min(n,60) y <- dbeta(CONST_i, x+BAYESIAN_FITTED[index], n+BAYESIAN_FITTED[index]-x)*(1-p)*p*exp(sigma_1)/({1-p}^2+2*p*{1-p}*exp(sigma_1)+{p^2}*exp(2*sigma_1)) if (!sum(is.na(y))) { tmp <- approx(sigma_1, y, sigma_s)$y return(tmp / sum(tmp) / (2 * max_sigma / (length_sigma - 1))) } else { return(NA) } } else { return(NA) } } #' Group BASELINe PDFs #' #' \code{groupBaseline} convolves groups of BASELINe posterior probability density #' functions (PDFs) to get combined PDFs for each group. #' #' @param baseline \code{Baseline} object containing the \code{db} and the #' BASELINe posterior probability density functions #' (PDF) for each of the sequences, as returned by #' \link{calcBaseline}. #' @param groupBy The columns in the \code{db} slot of the \code{Baseline} #' object by which to group the sequence PDFs. #' @param nproc number of cores to distribute the operation over. If #' \code{nproc} = 0 then the \code{cluster} has already been #' set and will not be reset. #' #' @return A \link{Baseline} object, containing the modified \code{db} and the BASELINe #' posterior probability density functions (PDF) for each of the groups. #' #' @details #' While the selection strengths predicted by BASELINe perform well on average, #' the estimates for individual sequences can be highly variable, especially when the #' number of mutations is small. #' #' To overcome this, PDFs from sequences grouped by biological or experimental relevance, #' are convolved to from a single PDF for the selection strength. For example, sequences #' from each sample may be combined together, allowing you to compare selection across #' samples. This is accomplished through a fast numerical convolution technique. #' #' @seealso To generate the \link{Baseline} object see \link{calcBaseline}. #' To calculate BASELINe statistics, such as the mean selection strength #' and the 95\% confidence interval, see \link{summarizeBaseline}. #' #' @references #' \enumerate{ #' \item Yaari G, et al. Quantifying selection in high-throughput immunoglobulin #' sequencing data sets. #' Nucleic Acids Res. 2012 40(17):e134. #' (Corrections at http://selection.med.yale.edu/baseline/correction/) #' } #' #' @examples #' \dontrun{ #' # Subset example data from alakazam as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' set.seed(112) #' db <- dplyr::slice_sample(db, n=200) #' #' # Collapse clones #' db <- collapseClones(db, cloneColumn="clone_id", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # Calculate BASELINe #' baseline <- calcBaseline(db, #' sequenceColumn="clonal_sequence", #' germlineColumn="clonal_germline", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc=1) #' #' # Group PDFs by sample #' grouped1 <- groupBaseline(baseline, groupBy="sample_id") #' sample_colors <- c("-1h"="steelblue", "+7d"="firebrick") #' plotBaselineDensity(grouped1, idColumn="sample_id", colorValues=sample_colors, #' sigmaLimits=c(-1, 1)) #' #' # Group PDFs by both sample (between variable) and isotype (within variable) #' grouped2 <- groupBaseline(baseline, groupBy=c("sample_id", "c_call")) #' isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", #' "IGHG"="seagreen", "IGHA"="steelblue") #' plotBaselineDensity(grouped2, idColumn="sample_id", groupColumn="c_call", #' colorElement="group", colorValues=isotype_colors, #' sigmaLimits=c(-1, 1)) #' # Collapse previous isotype (within variable) grouped PDFs into sample PDFs #' grouped3 <- groupBaseline(grouped2, groupBy="sample_id") #' sample_colors <- c("-1h"="steelblue", "+7d"="firebrick") #' plotBaselineDensity(grouped3, idColumn="sample_id", colorValues=sample_colors, #' sigmaLimits=c(-1, 1)) #' } #' @export groupBaseline <- function(baseline, groupBy, nproc=1) { # Hack for visibility of foreach index variables i <- NULL # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # Get indices of unique combinations of field(s) specified by groupBy # unique groups # crucial to use data.frame and assign colnames (esp. when groupBy has length 1) uniqueGroups <- data.frame(unique(baseline@db[, groupBy])) colnames(uniqueGroups) <- groupBy rownames(uniqueGroups) <- NULL # indices # crucial to have simplify=FALSE # (otherwise won't return a list if uniqueClones has length 1) uniqueGroupsIdx <- sapply(1:nrow(uniqueGroups), function(i){ curGroup <- data.frame(uniqueGroups[i, ]) colnames(curGroup) <- groupBy # match for each field curIdx <- sapply(groupBy, function(coln){ baseline@db[, coln]==curGroup[, coln] }, simplify=FALSE) curIdx <- do.call(rbind, curIdx) # intersect to get match across fields curIdx <- which(colSums(curIdx)==length(groupBy)) }, simplify=FALSE) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. baseline@db <- data.frame() gc() if (nproc > 1){ cluster <- parallel::makeCluster(nproc, type = "PSOCK") parallel::clusterExport( cluster, list('baseline', 'uniqueGroupsIdx', 'break2chunks', 'PowersOfTwo', 'convolutionPowersOfTwo', 'convolutionPowersOfTwoByTwos', 'weighted_conv', 'calculate_bayesGHelper', 'groupPosteriors', 'fastConv'), envir=environment() ) registerDoParallel(cluster, cores=nproc) } else if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } # Print status to console cat("Grouping BASELINe probability density functions...\n") # Number of total groups numbOfTotalGroups <- length(uniqueGroupsIdx) list_pdfs <- list() regions <- baseline@regions # Initialize numbOfSeqs # This holds the number of non NA sequences numbOfSeqs <- matrix(NA, ncol=length(baseline@regions), nrow=numbOfTotalGroups, dimnames=list(1:numbOfTotalGroups, regions)) templateBinom <- numbOfSeqs # For every region (e.g. CDR, FWR etc.) for (region in regions) { # Group (convolute) all the PDFS and get one single PDF list_region_pdfs <- foreach(i=1:numbOfTotalGroups) %dopar% { idx <- uniqueGroupsIdx[[i]] # Get a matrix (r=numb of sequences/groups * c=4001(i,e. the length of the PDFS)) # Care was taken to make sure that @pdfs[[region]] should be maintained # as a matrix regardless of the number of input sequences (even for a # single-sequence input) # Thus matrix_GroupPdfs should be expected to be maintained as a matrix as # opposed a numeric vector matrix_GroupPdfs <- (baseline@pdfs[[region]])[idx, , drop=FALSE] stopifnot(is(matrix_GroupPdfs, "matrix")) # A list version of list_GroupPdfs <- lapply( 1:nrow(matrix_GroupPdfs), function(rowIndex) { rowVals <- matrix_GroupPdfs[rowIndex, ] if( !all(is.na(rowVals)) ) { matrix_GroupPdfs[rowIndex, ] } }) rm(matrix_GroupPdfs) gc() # Determine the number of sequences that went into creating each of the PDFs # If running groupBaseline for the first time after calcBaseline, then # each PDF should have a numbOfSeqs=1. numbOfSeqs_region <- baseline@numbOfSeqs[idx, region] numbOfSeqs_region <- numbOfSeqs_region[numbOfSeqs_region > 0] if(any(numbOfSeqs_region>0)) { names(numbOfSeqs_region) <- 1:length(numbOfSeqs_region) } list_GroupPdfs <- list_GroupPdfs[!unlist(lapply(list_GroupPdfs, function(x) { any(is.na(x)) }))] list_GroupPdfs <- Filter(Negate(is.null), list_GroupPdfs) numbOfNonNASeqs <- length(list_GroupPdfs) # If all the PDFs in the group are NAs, return a PDF of NAs if (length(list_GroupPdfs) == 0) { return(c(rep(NA, 4001), 0)) } # If all the PDFs in the group have a numbOfSeqs=1 then # call groupPosteriors, which groups PDFs with equal weight if (sum(numbOfSeqs_region) == length(numbOfSeqs_region)) { return(c(groupPosteriors(list_GroupPdfs), numbOfNonNASeqs ) ) } # If all the PDFs in the group different numbOfSeqs then call # combineWeightedPosteriors, which groups PDFs weighted by the number of seqs/PDFs # that went into creating those PDFs if (sum(numbOfSeqs_region) > length(numbOfSeqs_region)) { # sort by number of items len_numbOfSeqs_region <- length(numbOfSeqs_region) sorted_numbOfSeqs_region <- sort(numbOfSeqs_region) rm(numbOfSeqs_region) gc() sorted_list_GroupPdfs <- list() for(newIndex in 1:len_numbOfSeqs_region){ sorted_list_GroupPdfs[[newIndex]] <- list_GroupPdfs[[ as.numeric(names(sorted_numbOfSeqs_region)[newIndex]) ]] } # Group all the PDFs that are created with the equal numbers of seqs/PDFs (i.e. of equal weight) repeat { # Count the numb of PDFs with the same weights table_sorted_numbOfSeqs_region <- table(sorted_numbOfSeqs_region) # Weight of interest (the first in the list) pdfWeight <- names(table_sorted_numbOfSeqs_region[table_sorted_numbOfSeqs_region>1])[1] if(is.na(pdfWeight)) { break } # The corresponding idexes of these PDFs with the same weight indexesOfWeight <- which(sorted_numbOfSeqs_region==pdfWeight) # Convolute these PDFs together list_sameWeightPdfs <- sorted_list_GroupPdfs[indexesOfWeight] updatedPdf <- groupPosteriors(list_sameWeightPdfs) rm(list_sameWeightPdfs) # The new updated weights for this convoluted PDF updatedWeight <- as.numeric(pdfWeight) * length(indexesOfWeight) # remove these from sorted_numbOfSeqs_region & sorted_list_GroupPdfs sorted_numbOfSeqs_region <- sorted_numbOfSeqs_region[-indexesOfWeight] sorted_list_GroupPdfs <- sorted_list_GroupPdfs[-indexesOfWeight] rm(indexesOfWeight) # add the convoluted PDF and its new weight newLength <- length(sorted_numbOfSeqs_region)+1 sorted_numbOfSeqs_region[newLength] <- updatedWeight sorted_list_GroupPdfs[[newLength]] <- updatedPdf rm(updatedWeight) rm(updatedPdf) gc() # sort by number of items len_sorted_numbOfSeqs_region <- length(sorted_numbOfSeqs_region) sorted_numbOfSeqs_region <- sort(sorted_numbOfSeqs_region) names(sorted_numbOfSeqs_region) <- as.character(1:len_sorted_numbOfSeqs_region) list_GroupPdfs <- sorted_list_GroupPdfs sorted_list_GroupPdfs <- list() for(newIndex in 1:len_numbOfSeqs_region){ sorted_list_GroupPdfs[[newIndex]] <- list_GroupPdfs[[ as.numeric(names(sorted_numbOfSeqs_region)[newIndex]) ]] } table_sorted_numbOfSeqs_region <- table(sorted_numbOfSeqs_region) if(sum(table_sorted_numbOfSeqs_region>1)>0){ break } } #return( c( groupPosteriors(sorted_list_GroupPdfs), 10 ) ) # Do pairwise grouping of PDFs based on weight # 1. sort by weights # 2. group the lowest two weighted PDFs # 3. resort, and repeat till you get one PDFs if(length(list_GroupPdfs)>1){ repeat{ updatedPdf <- combineWeightedPosteriors(list_GroupPdfs[[1]], sorted_numbOfSeqs_region[1], list_GroupPdfs[[2]], sorted_numbOfSeqs_region[2]) updatedWeight <- sorted_numbOfSeqs_region[1] + sorted_numbOfSeqs_region[2] # remove these from sorted_numbOfSeqs_region & sorted_list_GroupPdfs sorted_numbOfSeqs_region <- sorted_numbOfSeqs_region[-c(1,2)] sorted_list_GroupPdfs <- sorted_list_GroupPdfs[-c(1,2)] # add the convoluted PDF and its new weight newLength <- length(sorted_numbOfSeqs_region)+1 sorted_numbOfSeqs_region[newLength] <- updatedWeight rm(updatedWeight) sorted_list_GroupPdfs[[newLength]] <- updatedPdf rm(updatedPdf) gc() # sort by number of items len_sorted_numbOfSeqs_region <- length(sorted_numbOfSeqs_region) sorted_numbOfSeqs_region <- sort(sorted_numbOfSeqs_region) names(sorted_numbOfSeqs_region) <- as.character(1:len_sorted_numbOfSeqs_region) list_GroupPdfs <- sorted_list_GroupPdfs sorted_list_GroupPdfs <- list() for(newIndex in 1:len_numbOfSeqs_region){ sorted_list_GroupPdfs[[newIndex]] <- list_GroupPdfs[[ as.numeric(names(sorted_numbOfSeqs_region)[newIndex]) ]] } if(length(list_GroupPdfs)==1){ break } } } return( c( list_GroupPdfs[[1]], as.numeric(sorted_numbOfSeqs_region) ) ) } } # Convert the list of the region's PDFs into a matrix matrix_region_pdfs <- do.call(rbind, lapply(list_region_pdfs, function(x) { length(x) <- 4002 return(x) })) # Normalize and save PDF matrix # Hardcode normalization to max_sigma=20 and sigma_length=4001 pdf_norm <- 2*20 / 4000 pdf_mat <- matrix_region_pdfs[, 1:4001, drop=FALSE] list_pdfs[[region]] <- pdf_mat / rowSums(pdf_mat, na.rm=TRUE) / pdf_norm # Save regions numbOfSeqs[, region] <- matrix_region_pdfs[, 4002] } #colnames(numbOfSeqs) <- paste0("NUMB_SEQUENCES_", colnames(numbOfSeqs)) # Create the db, which will now contain the group information stopifnot(is.data.frame(uniqueGroups)) db <- uniqueGroups # Create a Baseline object with the above results to return baseline <- createBaseline(description="", db=as.data.frame(db), regionDefinition=baseline@regionDefinition, testStatistic=baseline@testStatistic, regions=regions, numbOfSeqs=numbOfSeqs, binomK=templateBinom, binomN=templateBinom, binomP=templateBinom, pdfs=list_pdfs) # Calculate BASELINe stats and update slot baseline <- summarizeBaseline(baseline) # Stop cluster if(nproc > 1) { parallel::stopCluster(cluster) } return(baseline) } #' Calculate BASELINe summary statistics #' #' \code{summarizeBaseline} calculates BASELINe statistics such as the mean selection #' strength (mean Sigma), the 95\% confidence intervals and p-values for the presence of #' selection. #' #' @param baseline \code{Baseline} object returned by \link{calcBaseline} containing #' annotations and BASELINe posterior probability density functions #' (PDFs) for each sequence. #' @param returnType One of \code{c("baseline", "df")} defining whether #' to return a \code{Baseline} object ("baseline") with an updated #' \code{stats} slot or a data.frame ("df") of summary statistics. #' @param nproc number of cores to distribute the operation over. If #' \code{nproc} = 0 then the \code{cluster} has already been #' set and will not be reset. #' #' @return Either a modified \code{Baseline} object or data.frame containing the #' mean BASELINe selection strength, its 95\% confidence intervals, and #' a p-value for the presence of selection. #' #' @details The returned p-value can be either positive or negative. Its magnitude #' (without the sign) should be interpreted as per normal. Its sign indicates #' the direction of the selection detected. A positive p-value indicates positive #' selection, whereas a negative p-value indicates negative selection. #' #' @seealso See \link{calcBaseline} for generating \code{Baseline} objects and #' \link{groupBaseline} for convolving groups of BASELINe PDFs. #' #' @references #' \enumerate{ #' \item Uduman M, et al. Detecting selection in immunoglobulin sequences. #' Nucleic Acids Res. 2011 39(Web Server issue):W499-504. #' } #' #' @examples #' \donttest{ #' # Subset example data #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHG") #' set.seed(112) #' db <- dplyr::slice_sample(db, n=100) #' #' # Collapse clones #' db <- collapseClones(db, cloneColumn="clone_id", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # Calculate BASELINe #' baseline <- calcBaseline(db, #' sequenceColumn="clonal_sequence", #' germlineColumn="clonal_germline", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc = 1) #' #' # Grouping the PDFs by the sample annotation #' grouped <- groupBaseline(baseline, groupBy="sample_id") #' #' # Get a data.frame of the summary statistics #' stats <- summarizeBaseline(grouped, returnType="df") #' } #' @export summarizeBaseline <- function(baseline, returnType=c("baseline", "df"), nproc=1) { # Hack for visibility of foreach index variable idx <- NULL # Check arguments returnType <- match.arg(returnType) # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc > 1){ cluster <- parallel::makeCluster(nproc, type="PSOCK") parallel::clusterExport(cluster, list('baseline', 'baselineSigma', 'baselineCI', 'baselinePValue'), envir=environment()) registerDoParallel(cluster, cores=nproc) } else if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } # Printing status to console cat("Calculating BASELINe statistics...\n") # Calculate stats for each sequence/group numbOfTotalSeqs <- nrow(baseline@db) regions <- baseline@regions db <- baseline@db if ("sequence_id" %in% colnames(db)) { db <- subset(db, select="sequence_id") } else if ("SEQUENCE_ID" %in% colnames(db)) { db <- subset(db, select="SEQUENCE_ID") } list_stats <- foreach(idx=iterators::icount(numbOfTotalSeqs)) %dopar% { df_baseline_seq <- data.frame() db_seq <- data.frame(db[idx, ]) names(db_seq) <- names(db) for (region in regions) { # care was taken to make sure that @pdfs[[region]] should be maintained # as a matrix regardless of the number of input sequences (even for a # single-sequence input) stopifnot(is(baseline@pdfs[[region]], "matrix")) baseline_pdf <- baseline@pdfs[[region]][idx, ] baseline_ci <- baselineCI(baseline_pdf) df_baseline_seq_region <- data.frame(db_seq, region=factor(region, levels=regions), baseline_sigma=baselineSigma(baseline_pdf), baseline_ci_lower=baseline_ci[1], baseline_ci_upper=baseline_ci[2], baseline_ci_pvalue=baselinePValue(baseline_pdf)) df_baseline_seq <- dplyr::bind_rows(df_baseline_seq, df_baseline_seq_region) } df_baseline_seq[,1] <- as.vector(unlist(df_baseline_seq[,1])) df_baseline_seq[,2] <- as.vector(unlist(df_baseline_seq[,2])) return(df_baseline_seq) } # Stop cluster if (nproc > 1) { parallel::stopCluster(cluster) } # Convert list of BASELINe stats into a data.frame stats <- as.data.frame(dplyr::bind_rows(list_stats)) if (returnType == "df") { return(stats) } else if (returnType == "baseline") { # Append stats to baseline object return(editBaseline(baseline, field="stats", stats)) } else { return(NULL) } } #' Two-sided test of BASELINe PDFs #' #' \code{testBaseline} performs a two-sample signifance test of BASELINe #' posterior probability density functions (PDFs). #' #' @param baseline \code{Baseline} object containing the \code{db} and grouped #' BASELINe PDFs returned by \link{groupBaseline}. #' @param groupBy string defining the column in the \code{db} slot of the #' \code{Baseline} containing sequence or group identifiers. #' #' @return A data.frame with test results containing the following columns: #' \itemize{ #' \item \code{region}: sequence region, such as \code{cdr} and \code{fwr}. #' \item \code{test}: string defining the groups be compared. The #' string is formated as the conclusion associated with the #' p-value in the form \code{GROUP1 != GROUP2}. Meaning, #' the p-value for rejection of the null hypothesis that #' GROUP1 and GROUP2 have equivalent distributions. #' \item \code{pvalue}: two-sided p-value for the comparison. #' \item \code{fdr}: FDR corrected \code{pvalue}. #' } #' #' @seealso To generate the \link{Baseline} input object see \link{groupBaseline}. #' #' @references #' \enumerate{ #' \item Yaari G, et al. Quantifying selection in high-throughput immunoglobulin #' sequencing data sets. #' Nucleic Acids Res. 2012 40(17):e134. #' (Corretions at http://selection.med.yale.edu/baseline/correction/) #' } #' #' @examples #' \donttest{ #' # Subset example data as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' set.seed(112) #' db <- dplyr::slice_sample(db, n=200) #' #' # Collapse clones #' db <- collapseClones(db, cloneColumn="clone_id", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # Calculate BASELINe #' baseline <- calcBaseline(db, #' sequenceColumn="clonal_sequence", #' germlineColumn="clonal_germline", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc=1) #' #' # Group PDFs by the isotype #' grouped <- groupBaseline(baseline, groupBy="c_call") #' #' # Visualize isotype PDFs #' plot(grouped, "c_call") #' #' # Perform test on isotype PDFs #' testBaseline(grouped, groupBy="c_call") #' } #' @export testBaseline <- function(baseline, groupBy) { ## DEBUG # baseline=grouped; groupBy="sample_id" # Get test groups groups <- as.character(baseline@db[[groupBy]]) if (length(groups) < 2) { stop('The ', groupBy, ' column does not contain at least two groups.') } pair_indices <- combn(1:length(groups), 2, simplify=F) pair_names <- combn(groups, 2, simplify=F) test_names <- sapply(pair_names, paste, collapse=" != ") # Run tests test_list <- list() for (n in baseline@regions) { d <- baseline@pdfs[[n]] p <- sapply(pair_indices, function(x) { baseline2DistPValue(d[x[1], ], d[x[2], ])}) test_list[[n]] <- data.frame(test=test_names, pvalue=p) } test_df <- bind_rows(test_list, .id="region") test_df$fdr <- p.adjust(test_df$pvalue, method="fdr") return(test_df) } # Calculate mean sigma of a BASELINe PDF # # @param base BASLINe PDF vector. # @param max_sigma maximum sigma score. # @param length_sigma length of the PDF vector. # @return Mean sigma. baselineSigma <- function(base, max_sigma=20, length_sigma=4001) { # Return NA on bad input if (any(is.na(base))) { return(NA) } sigma_s <- seq(-max_sigma, max_sigma, length.out=length_sigma) norm <- sum(base, na.rm=TRUE) sigma_mean <- base %*% sigma_s / norm return(sigma_mean) } # Calculate confidence interval for BASELINe PDF # # @param base BASLINe PDF vector. # @param low lower CI percentile. # @param up upper CI percentile. # @param max_sigma maximum sigma score. # @param length_sigma length of the PDF vector. # @return A vector of \code{c(lower, upper)} confidence bounds. baselineCI <- function (base, low=0.025, up=0.975, max_sigma=20, length_sigma=4001){ # Return NA on bad input if (any(is.na(base))) { return(c(NA, NA)) } sigma_s <- seq(-max_sigma, max_sigma, length.out=length_sigma) cdf <- cumsum(base) cdf <- cdf / cdf[length(cdf)] intervalLow <- findInterval(low, cdf) fractionLow <- (low - cdf[intervalLow])/(cdf[intervalLow + 1] - cdf[intervalLow]) intervalUp <- findInterval(up,cdf) fractionUp <- (up - cdf[intervalUp]) / (cdf[intervalUp] - cdf[intervalUp - 1]) sigmaLow <- sigma_s[intervalLow] + fractionLow*(sigma_s[intervalLow + 1] - sigma_s[intervalLow]) sigmaUp <- sigma_s[intervalUp] + fractionUp*(sigma_s[intervalUp + 1] - sigma_s[intervalUp]) return(c(sigmaLow, sigmaUp)) } # Calculate a p-value that the given BASELINe PDF differs from zero # # @param base BASLINe PDF vector. # @param max_sigma maximum sigma score. # @param length_sigma length of the PDF vector. # @return A p-value. The returned p-value can be either positive or negative. # Its magnitude (without the sign) should be interpreted as per normal. # Its sign indicate the direction of the selection detected. A positive # p-value indicates positive selection, whereas a negative p-value # indicates negative selection. baselinePValue <- function (base, length_sigma=4001, max_sigma=20){ # note: since there isn't a null distribution, this "p-value" isn't a p-value in the # conventional sense if (!any(is.na(base))) { # normalization factor #norm <- (length_sigma - 1) / 2 / max_sigma # sums up to 100 for default setting (sigma_s from -20 to 20 with length 4001) norm <- sum(base, na.rm=TRUE) # compute Pr(selection strength < 0): # sum up density for sigma from min_sigma up to and right before 0 (area under curve), plus # + binomial correction (density at sigma=0 divided by 2); # normalized pvalue <- ( sum(base[1:((length_sigma - 1) / 2)]) + base[((length_sigma + 1) / 2)] / 2 ) / norm # from Fig 4 caption of Detecting selection in immunoglobulin sequences by Uduman et al. 2011 # "Note that P values less than zero are indicative of negative selection." # 1) if Pr(selection strength < 0) <= 0.5, return Pr(selection strength < 0) # this will be positive, and serves as the "p-value" for positive selection # 2) if Pr(selection strength < 0) > 0.5, return -Pr(selection strength>0) # this will be negative, and serves as the "p-value" for negative selection # (negative sign highlights the fact that selection is negative) if (pvalue > 0.5) { pvalue <- -(1 - pvalue) } } else { pvalue <- NA } return(pvalue) } # Compute p-value of two BASELINe PDFs # # @param base1 first selection PDF; must be a numeric vector # @param base2 second selection PDF; must be a numeric vector # @return Two-sided p-value that base1 and base2 differ. baseline2DistPValue <-function(base1, base2) { # NOTE: make sure to supply 2 vectors (not 1-row data.frames) when # calling this function directly ## Debug # base1=grouped@pdfs[["CDR"]][1, ]; base2=grouped@pdfs[["FWR"]][1, ] # Get lengths len1 <- length(base1) len2 <- length(base2) # Check input if (len1 != len2) { stop("base1 and base2 must be the same length.") } # NA if all values in pdfs are NA if (sum(is.na(base1))==len1 | sum(is.na(base2))==len2) { return(NA) } # Determine p-value if (len1 > 1) { # Normalize base1 <- base1 / sum(base1, na.rm=TRUE) base2 <- base2 / sum(base2, na.rm=TRUE) # Calculate p-value cum2 <- cumsum(base2) - base2/2 pvalue <- sum(base1*cum2) if (pvalue > 0.5) { pvalue <- 1 - pvalue } } else { pvalue <- NA } return(pvalue) } #### Plotting functions #### #' Plots BASELINe probability density functions #' #' \code{plotBaselineDensity} plots the probability density functions resulting from selection #' analysis using the BASELINe method. #' #' @param baseline \code{Baseline} object containing selection probability #' density functions. #' @param idColumn name of the column in the \code{db} slot of \code{baseline} #' containing primary identifiers. #' @param groupColumn name of the column in the \code{db} slot of \code{baseline} #' containing secondary grouping identifiers. If \code{NULL}, #' organize the plot only on values in \code{idColumn}. #' @param colorElement one of \code{c("id", "group")} specifying whether the #' \code{idColumn} or \code{groupColumn} will be used for color coding. #' The other entry, if present, will be coded by line style. #' @param colorValues named vector of colors for entries in \code{colorElement}, with #' names defining unique values in the \code{colorElement} column and values #' being colors. Also controls the order in which values appear on the #' plot. If \code{NULL} alphabetical ordering and a default color palette #' will be used. #' @param facetBy one of \code{c("region", "group")} specifying which category to facet the #' plot by, either values in \code{groupColumn} ("group") or regions #' defined in the \code{regions} slot of the \code{baseline} object ("region"). #' If this is set to "group", then the region will behave as the \code{groupColumn} #' for purposes of the \code{colorElement} argument. #' @param title string defining the plot title. #' @param subsetRegions character vector defining a subset of regions to plot, correspoding #' to the regions for which the \code{baseline} data was calculated. If #' \code{NULL} all regions in \code{baseline} are plotted. #' @param sigmaLimits numeric vector containing two values defining the \code{c(lower, upper)} #' bounds of the selection scores to plot. #' @param style type of plot to draw. One of: #' \itemize{ #' \item \code{"density"}: plots a set of curves for each probability #' density function in \code{baseline}, #' with colors determined by values in the #' \code{colorElement} column. #' Faceting is determined by the #' \code{facetBy} argument. #' } #' @param sizeElement one of \code{c("none", "id", "group")} specifying whether the lines in the #' plot should be all of the same size (\code{none}) or have their sizes depend on #' the values in \code{id} or \code{code}. #' @param size numeric scaling factor for lines, points and text in the plot. #' @param silent if \code{TRUE} do not draw the plot and just return the ggplot2 #' object; if \code{FALSE} draw the plot. #' @param ... additional arguments to pass to ggplot2::theme. #' #' @return A ggplot object defining the plot. #' #' @seealso Takes as input a \link{Baseline} object returned from \link{groupBaseline}. #' #' @examples #' \dontrun{ #' # Subset example data as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' set.seed(112) #' db <- dplyr::slice_sample(db, n=100) #' #' # Collapse clones #' db <- collapseClones(db, cloneColumn="clone_id", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # Calculate BASELINe #' baseline <- calcBaseline(db, #' sequenceColumn="clonal_sequence", #' germlineColumn="clonal_germline", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc=1) #' #' # Grouping the PDFs by the sample and isotype annotations #' grouped <- groupBaseline(baseline, groupBy=c("sample_id", "c_call")) #' #' # Plot density faceted by region with custom isotype colors #' isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", #' "IGHG"="seagreen", "IGHA"="steelblue") #' plotBaselineDensity(grouped, "sample_id", "c_call", colorValues=isotype_colors, #' colorElement="group", sigmaLimits=c(-1, 1)) #' #' # Facet by isotype instead of region #' sample_colors <- c("-1h"="steelblue", "+7d"="firebrick") #' plotBaselineDensity(grouped, "sample_id", "c_call", facetBy="group", #' colorValues=sample_colors, sigmaLimits=c(-1, 1)) #' } #' #' @export plotBaselineDensity <- function(baseline, idColumn, groupColumn=NULL, colorElement=c("id", "group"), colorValues=NULL, title=NULL, subsetRegions=NULL, sigmaLimits=c(-5, 5), facetBy=c("region", "group"), style=c("density"), sizeElement=c("none", "id", "group"), size=1, silent=FALSE, ...) { ## DEBUG # baseline=grouped # idColumn="sample_id"; groupColumn="c_call"; subsetRegions=NULL; sigmaLimits=c(-5, 5) # facetBy="region"; style="density"; size=1; silent=FALSE # Check input colorElement <- match.arg(colorElement) style <- match.arg(style) facetBy <- match.arg(facetBy) sizeElement <- match.arg(sizeElement) # Set base plot settings base_theme <- theme_bw() + theme(panel.background=element_blank(), panel.grid.major=element_blank(), panel.grid.minor=element_blank(), panel.border=element_rect(color="black", linewidth = 0.5)) + theme(strip.background=element_rect(fill="white", color="black", linewidth=0.5)) if (style == "density") { # Check for proper grouping if (any(duplicated(baseline@db[, c(idColumn, groupColumn)]))) { stop("More than one unique annotation set per summary statistic. Rerun groupBaseline to combine data.") } # Subset to regions of interest dens_names <- baseline@regions if (!is.null(subsetRegions)) { dens_names <- dens_names[dens_names %in% subsetRegions] } dens_list <- baseline@pdfs[dens_names] # Get row and column names for PDF matrices group_df <- subset(baseline@db, select=c(idColumn, groupColumn)) group_df$GROUP_COLLAPSE <- apply(subset(group_df, select=c(idColumn, groupColumn)), 1, paste, collapse=",") col_names <- seq(-20, 20, length.out=ncol(dens_list[[1]])) # Update column and rownames for PDF matrices and subset to Sigma in -5:5 for (i in 1:length(dens_list)) { rownames(dens_list[[i]]) <- group_df$GROUP_COLLAPSE colnames(dens_list[[i]]) <- col_names dens_list[[i]] <- dens_list[[i]][, col_names >= sigmaLimits[1] & col_names <= sigmaLimits[2], drop=FALSE] } # Melt density matrices melt_list <- list() for (n in dens_names) { tmp_df <- as.data.frame(dens_list[[n]]) tmp_df$GROUP_COLLAPSE <- rownames(dens_list[[n]]) gather_cols <- names(tmp_df)[names(tmp_df) != "GROUP_COLLAPSE"] melt_list[[n]] <- tidyr::gather(tmp_df, "SIGMA", "DENSITY", tidyselect::all_of(gather_cols), convert=TRUE) } dens_df <- dplyr::bind_rows(melt_list, .id="region") # Assign id and group columns to density data.frame dens_df[, idColumn] <- group_df[match(dens_df$GROUP_COLLAPSE, group_df$GROUP_COLLAPSE), idColumn] if (!is.null(groupColumn)) { dens_df[, groupColumn] <- group_df[match(dens_df$GROUP_COLLAPSE, group_df$GROUP_COLLAPSE), groupColumn] } # Set secondary grouping and faceting columns if (facetBy == "group") { secondaryColumn <- "region" facetColumn <- groupColumn } else if (facetBy == "region") { secondaryColumn <- groupColumn facetColumn <- "region" } # Apply color order if (!is.null(colorValues)) { if (colorElement == "id") { dens_df[, idColumn] <- factor(dens_df[, idColumn], levels=names(colorValues)) } else { dens_df[, groupColumn] <- factor(dens_df[, groupColumn], levels=names(colorValues)) } } # Apply line width dens_df[, "size"] <- factor(1) if (sizeElement=="id") { dens_df[, "size"] <- factor(dens_df[, idColumn]) } else if (sizeElement == "group" ) { dens_df[, "size"] <- factor(dens_df[, groupColumn]) } size_values <- 1:length(levels(dens_df[,"size"])) size_names <- levels(dens_df[, "size"]) size_values <- size*size_values/max(size_values) names(size_values) <- size_names # Plot probability density curve p1 <- ggplot(dens_df, aes(x=!!rlang::sym("SIGMA"), y=!!rlang::sym("DENSITY"))) + base_theme + xlab(expression(Sigma)) + ylab("Density") + geom_line(aes(linewidth=!!rlang::sym("size"))) + scale_discrete_manual("linewidth", values = size_values) # Add line if (colorElement == "id" & is.null(secondaryColumn)) { p1 <- p1 + aes(color=!!rlang::sym(idColumn)) } else if (colorElement == "id" & !is.null(secondaryColumn)) { p1 <- p1 + aes(color=!!rlang::sym(idColumn), linetype=!!rlang::sym(secondaryColumn)) } else if (colorElement == "group") { p1 <- p1 + aes(color=!!rlang::sym(secondaryColumn), linetype=!!rlang::sym(idColumn)) } else { stop("Incompatible arguments for groupColumn, colorElement and facetBy") } # Add colors if (!is.null(colorValues)) { p1 <- p1 + scale_color_manual(values=colorValues) } # Add title if (!is.null(title)) { p1 <- p1 + ggtitle(title) } # Add facet if (is.null(facetColumn)) { stop("Cannot facet by group if groupColumn=NULL") } else { p1 <- p1 + facet_grid(paste(facetColumn, "~ .")) } } # Add additional theme elements p1 <- p1 + scale_size_manual(breaks=names(size_values), values=size_values) if (sizeElement == "none") { p1 <- p1 + guides(linewidth="none", colour = guide_legend(override.aes=list(linewidth=size_values))) if (length(unique(c(groupColumn, idColumn))) > 1) { p1 <- p1 + guides (linetype=guide_legend(override.aes=list(linewidth=size_values))) } } else if (sizeElement == colorElement) { p1 <- p1 + guides(linewidth="none", colour = guide_legend(override.aes = list(linewidth = size_values))) } else { p1 <- p1 + guides(linewidth="none", linetype = guide_legend(override.aes = list(linewidth = size_values))) } p1 <- p1 + do.call(theme, list(...)) # Plot if (!silent) { plot(p1) } invisible(p1) } #' Plots BASELINe summary statistics #' #' \code{plotBaselineSummary} plots a summary of the results of selection analysis #' using the BASELINe method. #' #' @param baseline either a data.frame returned from \link{summarizeBaseline} #' or a \code{Baseline} object returned from \link{groupBaseline} #' containing selection probability density functions and summary #' statistics. #' @param idColumn name of the column in \code{baseline} containing primary identifiers. #' If the input is a \code{Baseline} object, then this will be a column #' in the \code{stats} slot of \code{baseline}. #' @param groupColumn name of the column in \code{baseline} containing secondary grouping #' identifiers. If the input is a \code{Baseline} object, then this will #' be a column in the \code{stats} slot of \code{baseline}. #' @param groupColors named vector of colors for entries in \code{groupColumn}, with #' names defining unique values in the \code{groupColumn} and values #' being colors. Also controls the order in which groups appear on the #' plot. If \code{NULL} alphabetical ordering and a default color palette #' will be used. Has no effect if \code{facetBy="group"}. #' @param subsetRegions character vector defining a subset of regions to plot, correspoding #' to the regions for which the \code{baseline} data was calculated. If #' \code{NULL} all regions in \code{baseline} are plotted. #' @param facetBy one of c("group", "region") specifying which category to facet the #' plot by, either values in \code{groupColumn} ("group") or regions #' defined in \code{baseline} ("region"). The data that is not used #' for faceting will be color coded. #' @param title string defining the plot title. #' @param style type of plot to draw. One of: #' \itemize{ #' \item \code{"summary"}: plots the mean and confidence interval for #' the selection scores of each value in #' \code{idColumn}. Faceting and coloring #' are determine by values in \code{groupColumn} #' and regions defined in \code{baseline}, #' depending upon the \code{facetBy} argument. #' } #' @param size numeric scaling factor for lines, points and text in the plot. #' @param silent if \code{TRUE} do not draw the plot and just return the ggplot2 #' object; if \code{FALSE} draw the plot. #' @param ... additional arguments to pass to ggplot2::theme. #' #' @return A ggplot object defining the plot. #' #' @seealso Takes as input either a \link{Baseline} object returned by \link{groupBaseline} #' or a data.frame returned from \link{summarizeBaseline}. #' #' @examples #' \donttest{ #' # Subset example data as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' set.seed(112) #' db <- dplyr::slice_sample(db, n=25) #' #' # Collapse clones #' db <- collapseClones(db, cloneColumn="clone_id", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # Calculate BASELINe #' baseline <- calcBaseline(db, #' sequenceColumn="clonal_sequence", #' germlineColumn="clonal_germline", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc=1) #' #' # Grouping the PDFs by sample and isotype annotations #' grouped <- groupBaseline(baseline, groupBy=c("sample_id", "c_call")) #' #' # Plot mean and confidence interval by region with custom group colors #' isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", #' "IGHG"="seagreen", "IGHA"="steelblue") #' plotBaselineSummary(grouped, "sample_id", "c_call", #' groupColors=isotype_colors, facetBy="region") #' } #' #' @export plotBaselineSummary <- function(baseline, idColumn, groupColumn=NULL, groupColors=NULL, subsetRegions=NULL, facetBy=c("region", "group"), title=NULL, style=c("summary"), size=1, silent=FALSE, ...) { # Check arguments style <- match.arg(style) facetBy <- match.arg(facetBy) # Check input object if (is(baseline, "Baseline")) { stats_df <- baseline@stats } else if (is(baseline, "data.frame")) { stats_df <- baseline } else { stop("Input must be either a data.frame or Baseline object.") } # Check for required columns baseline_cols <- c("region", "baseline_sigma", "baseline_ci_lower", "baseline_ci_pvalue") if (!(all(baseline_cols %in% names(stats_df)))) { stop("Input must contain columns defined by summarizeBaseline.") } # Check for proper grouping if (any(duplicated(stats_df[, c(idColumn, groupColumn, "region")]))) { stop("More than one unique annotation set per summary statistic. Rerun groupBaseline to combine data.") } # Subset to regions of interest if (!is.null(subsetRegions)) { stats_df <- stats_df[stats_df$region %in% subsetRegions, ] } # Set base plot settings base_theme <- theme_bw() + theme(panel.background=element_blank(), panel.grid.major=element_blank(), panel.grid.minor=element_blank(), panel.border=element_rect(color="black", linewidth=0.5)) + theme(strip.background=element_rect(fill="white", color="black", linewidth=0.5)) + theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank()) + theme(legend.position="top") + theme(axis.text.x=element_text(angle=90, vjust=0.5, hjust=1)) if (style == "summary") { # Plot mean and confidence intervals stats_df <- stats_df[!is.na(stats_df$baseline_sigma), ] if (!is.null(groupColumn) & !is.null(groupColors)) { stats_df[,groupColumn] <- factor(stats_df[,groupColumn], levels=names(groupColors)) } p1 <- ggplot(stats_df, aes(x=!!rlang::sym(idColumn), y=!!rlang::sym("baseline_sigma"), ymax=max(!!rlang::sym("baseline_sigma")))) + base_theme + xlab("") + ylab(expression(Sigma)) + geom_hline(yintercept=0, linewidth=1*size, linetype=2, color="grey") + geom_point(size=3*size, position=position_dodge(0.6)) + geom_errorbar(aes(ymin=!!rlang::sym("baseline_ci_lower"), ymax=!!rlang::sym("baseline_ci_upper")), width=0.2, linewidth=0.5*size, alpha=0.8, position=position_dodge(0.6)) if (!is.null(title)) { p1 <- p1 + ggtitle(title) } if (is.null(groupColumn) & facetBy == "region") { p1 <- p1 + facet_grid(region ~ .) } else if (!is.null(groupColumn) & !is.null(groupColors) & facetBy == "region") { #groupColors <- factor(groupColors, levels=groupColors) p1 <- p1 + scale_color_manual(name=groupColumn, values=groupColors) + aes(color=!!rlang::sym(groupColumn)) + facet_grid(region ~ .) } else if (!is.null(groupColumn) & is.null(groupColors) & facetBy == "region") { p1 <- p1 + aes(color=!!rlang::sym(groupColumn)) + facet_grid(region ~ .) } else if (!is.null(groupColumn) & facetBy == "group") { p1 <- p1 + scale_color_manual(name="Region", values=REGION_PALETTE) + aes(color=!!rlang::sym("region")) + facet_grid(paste(groupColumn, "~ .")) } else { stop("Cannot facet by group if groupColumn=NULL") } } # Add additional theme elements p1 <- p1 + do.call(theme, list(...)) # Plot if (!silent) { plot(p1) } else { invisible(p1) } } #### Original BASELINe functions #### ##Covolution break2chunks<-function(G=1000){ base<-2^round(log(sqrt(G),2),0) return(c(rep(base,floor(G/base)-1),base+G-(floor(G/base)*base))) } PowersOfTwo <- function(G=100){ exponents <- array() i = 0 while(G > 0){ i=i+1 exponents[i] <- floor( log2(G) ) G <- G-2^exponents[i] } return(exponents) } convolutionPowersOfTwo <- function( cons, length_sigma=4001 ){ G = ncol(cons) if(G>1){ for(gen in log(G,2):1){ ll<-seq(from=2,to=2^gen,by=2) sapply(ll,function(l){cons[,l/2]<<-weighted_conv(cons[,l],cons[,l-1],length_sigma=length_sigma)}) } } return( cons[,1] ) } convolutionPowersOfTwoByTwos <- function( cons, length_sigma=4001,G=1 ){ if(length(ncol(cons))) G<-ncol(cons) groups <- PowersOfTwo(G) matG <- matrix(NA, ncol=length(groups), nrow=length(cons)/G ) startIndex = 1 for( i in 1:length(groups) ){ stopIndex <- 2^groups[i] + startIndex - 1 if(stopIndex!=startIndex){ matG[,i] <- convolutionPowersOfTwo( cons[,startIndex:stopIndex], length_sigma=length_sigma ) startIndex = stopIndex + 1 } else { if(G>1) matG[,i] <- cons[,startIndex:stopIndex] else matG[,i] <- cons #startIndex = stopIndex + 1 } } return( list( matG, groups ) ) } weighted_conv <- function(x, y, w=1, m=100, length_sigma=4001){ lx<-length(x) ly<-length(y) if({lx1){ while( i1 & Length_Postrior<=Threshold){ cons = matrix(unlist(listPosteriors),length(listPosteriors[[1]]),length(listPosteriors)) listMatG <- convolutionPowersOfTwoByTwos(cons,length_sigma=length_sigma) y<-calculate_bayesGHelper(listMatG,length_sigma=length_sigma) return( y/sum(y)/(2*max_sigma/(length_sigma-1)) ) }else if(Length_Postrior==1) return(listPosteriors[[1]]) else if(Length_Postrior==0) return(NA) else { cons = matrix(unlist(listPosteriors),length(listPosteriors[[1]]),length(listPosteriors)) y = fastConv(cons,max_sigma=max_sigma, length_sigma=length_sigma ) return( y/sum(y)/(2*max_sigma/(length_sigma-1)) ) } } fastConv<-function(cons, max_sigma=20, length_sigma=4001){ chunks<-break2chunks(G=ncol(cons)) if(ncol(cons)==3) chunks<-2:1 index_chunks_end <- cumsum(chunks) index_chunks_start <- c(1,index_chunks_end[-length(index_chunks_end)]+1) index_chunks <- cbind(index_chunks_start,index_chunks_end) case <- sum(chunks!=chunks[1]) if(case==1) End <- max(1,((length(index_chunks)/2)-1)) else End <- max(1,((length(index_chunks)/2))) firsts <- sapply(1:End,function(i){ indexes<-index_chunks[i,1]:index_chunks[i,2] convolutionPowersOfTwoByTwos(cons[ ,indexes])[[1]] }) if(case==0){ result<-calculate_bayesGHelper( convolutionPowersOfTwoByTwos(firsts) ) }else if(case==1){ last<-list(calculate_bayesGHelper( convolutionPowersOfTwoByTwos( cons[ ,index_chunks[length(index_chunks)/2,1]:index_chunks[length(index_chunks)/2,2]] ) ),0) result_first<-calculate_bayesGHelper(convolutionPowersOfTwoByTwos(firsts)) result<-calculate_bayesGHelper( list( cbind( result_first,last[[1]]), c(log(index_chunks_end[length(index_chunks)/2-1],2),log(index_chunks[length(index_chunks)/2,2]-index_chunks[length(index_chunks)/2,1]+1,2)) ) ) } return(as.vector(result)) } #' Calculate the BASELINe PDFs (including for regions that include CDR3 and FWR4) #' #' \code{calcBaseline} calculates the BASELINe posterior probability density #' functions (PDFs) for sequences in the given Change-O \code{data.frame}. #' #' @param db \code{data.frame} containing sequence data and annotations. #' @param sequenceColumn \code{character} name of the column in \code{db} #' containing input sequences. #' @param germlineColumn \code{character} name of the column in \code{db} #' containing germline sequences. #' @param testStatistic \code{character} indicating the statistical framework #' used to test for selection. One of #' \code{c("local", "focused", "imbalanced")}. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. #' @param targetingModel \link{TargetingModel} object. Default is \link{HH_S5F}. #' @param mutationDefinition \link{MutationDefinition} object defining replacement #' and silent mutation criteria. If \code{NULL} then #' replacement and silent are determined by exact #' amino acid identity. Note, if the input data.frame #' already contains observed and expected mutation frequency #' columns then mutations will not be recalculated and this #' argument will be ignored. #' @param calcStats \code{logical} indicating whether or not to calculate the #' summary statistics \code{data.frame} stored in the #' \code{stats} slot of a \link{Baseline} object. #' @param nproc number of cores to distribute the operation over. If #' \code{nproc=0} then the \code{cluster} has already been #' set and will not be reset. #' @param cloneColumn \code{character} name of the column in \code{db} #' containing clonal identifiers. Relevant only for #' when regionDefinition includes CDR and FWR4 (else #' this value can be \code{NULL}) #' @param juncLengthColumn \code{character} name of the column in \code{db} #' containing the junction length. Relevant only for #' when regionDefinition includes CDR and FWR4 (else #' this value can be \code{NULL}) #' @return A \link{Baseline} object containing the modified \code{db} and BASELINe #' posterior probability density functions (PDF) for each of the sequences. #' #' @details #' Calculates the BASELINe posterior probability density function (PDF) for #' sequences in the provided \code{db}. #' #' \strong{Note}: Individual sequences within clonal groups are not, strictly speaking, #' independent events and it is generally appropriate to only analyze selection #' pressures on an effective sequence for each clonal group. For this reason, #' it is strongly recommended that the input \code{db} contains one effective #' sequence per clone. Effective clonal sequences can be obtained by calling #' the \link{collapseClones} function. #' #' If the \code{db} does not contain the #' required columns to calculate the PDFs (namely mu_count & mu_expected) #' then the function will: #' \enumerate{ #' \item Calculate the numbers of observed mutations. #' \item Calculate the expected frequencies of mutations and modify the provided #' \code{db}. The modified \code{db} will be included as part of the #' returned \code{Baseline} object. #' } #' #' The \code{testStatistic} indicates the statistical framework used to test for selection. #' E.g. #' \itemize{ #' \item \code{local} = CDR_R / (CDR_R + CDR_S). #' \item \code{focused} = CDR_R / (CDR_R + CDR_S + FWR_S). #' \item \code{imbalanced} = CDR_R + CDR_S / (CDR_R + CDR_S + FWR_S + FRW_R). #' } #' For \code{focused} the \code{regionDefinition} must only contain two regions. If more #' than two regions are defined the \code{local} test statistic will be used. #' For further information on the frame of these tests see Uduman et al. (2011). #' #' @references #' \enumerate{ #' \item Hershberg U, et al. Improved methods for detecting selection by mutation #' analysis of Ig V region sequences. #' Int Immunol. 2008 20(5):683-94. #' \item Uduman M, et al. Detecting selection in immunoglobulin sequences. #' Nucleic Acids Res. 2011 39(Web Server issue):W499-504. #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso See \link{Baseline} for the return object. #' See \link{groupBaseline} and \link{summarizeBaseline} for further processing. #' See \link{plotBaselineSummary} and \link{plotBaselineDensity} for plotting results. #' #' @examples #' # Load and subset example data #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHG" & sample_id == "+7d") #' #' # Collapse clones #' db <- collapseClones(db, cloneColumn="clone_id", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # Calculate BASELINe #' baseline <- calcBaseline(db, #' sequenceColumn="clonal_sequence", #' germlineColumn="clonal_germline", #' testStatistic="focused", #' regionDefinition=IMGT_V, #' targetingModel=HH_S5F, #' nproc=1) #' #' @export calcBaseline <- function(db, sequenceColumn = "clonal_sequence", germlineColumn = "clonal_germline", testStatistic = c("local","focused", "imbalanced"), regionDefinition = NULL, targetingModel = HH_S5F, mutationDefinition = NULL, calcStats = FALSE, nproc = 1, # following are relevant only when regionDefinition includes CDR3 and FWR4: cloneColumn = NULL, juncLengthColumn = NULL) { # Hack for visibility of foreach index variable idx <- NULL # Evaluate argument choices testStatistic <- match.arg(testStatistic) check <- checkColumns(db, c(sequenceColumn, germlineColumn)) if (check != TRUE) { stop(check) } regionDefinitionName <- "" if (!is.null(regionDefinition)) { regionDefinitionName <- regionDefinition@name } # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Check mutation definition if (!is.null(mutationDefinition) & !is(mutationDefinition, "MutationDefinition")) { stop(deparse(substitute(mutationDefinition)), " is not a valid MutationDefinition object") } # Check targeting model if (!is(targetingModel, "TargetingModel")) { stop(deparse(substitute(targetingModel)), " is not a valid TargetingModel object") } # Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn, germlineColumn)) db$tmp_baseline_row_id <- 1:nrow(db) # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # nproc_arg will be passed to any function that has the nproc argument # If the cluster is already being set by the parent function then # this will be set to 'cluster', that way the child function does not close # the connections and reset the cluster. #nproc_arg <- nproc # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc > 1) { cluster <- parallel::makeCluster(nproc, type="PSOCK") parallel::clusterExport(cluster, list('db', 'sequenceColumn', 'germlineColumn', 'cloneColumn', 'juncLengthColumn', 'setRegionBoundaries', 'testStatistic', 'regionDefinition', 'targetingModel', 'mutationDefinition','calcStats', 'break2chunks', 'PowersOfTwo', 'convolutionPowersOfTwo', 'convolutionPowersOfTwoByTwos', 'weighted_conv', 'calculate_bayesGHelper', 'groupPosteriors', 'fastConv', 'calcBaselineHelper', 'c2s', 's2c', 'words', 'translate', 'calcBaselineBinomialPdf','CONST_I', 'BAYESIAN_FITTED', 'calcObservedMutations','NUCLEOTIDES', 'NUCLEOTIDES_AMBIGUOUS', 'IUPAC2nucs', 'makeNullRegionDefinition', 'getCodonPos','getContextInCodon', 'mutationType', 'AMINO_ACIDS','binMutationsByRegion', 'collapseMatrixToVector','calcExpectedMutations', 'calculateTargeting','HH_S5F','calculateMutationalPaths', 'CODON_TABLE','IMGT_V_BY_REGIONS'), envir=environment() ) registerDoParallel(cluster, cores=nproc) #nproc_arg <- cluster } else if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } # If db does not contain the required columns to calculate the PDFs (namely mu_count # & mu_expected mutations), then the function will: # 1. Calculate the numbers of observed mutations # 2. Calculate the expected frequencies of mutations # After that BASELINe prob. densities can be calcualted per sequence. if (is.null(regionDefinition)) { rd_labels <- makeNullRegionDefinition()@labels observedColumns <- paste0("mu_count_", rd_labels) expectedColumns <- paste0("mu_expected_", rd_labels) } else { observedColumns <- paste0("mu_count_", regionDefinition@labels) expectedColumns <- paste0("mu_expected_", regionDefinition@labels) } if (!all(c(observedColumns, expectedColumns) %in% colnames(db))) { # If the germlineColumn & sequenceColumn are not found in the db error and quit if (!all(c(sequenceColumn, germlineColumn) %in% colnames(db))) { stop(paste0("Both ", sequenceColumn, " & ", germlineColumn, " columns need to be present in the db")) } message(paste0("calcBaseline will calculate observed and expected mutations for ", sequenceColumn," using ", germlineColumn, " as a reference.")) # Calculate the numbers of observed mutations db <- observedMutations(db, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, regionDefinition=regionDefinition, mutationDefinition=mutationDefinition, frequency=FALSE, combine=FALSE, nproc=0, cloneColumn=cloneColumn, juncLengthColumn=juncLengthColumn) # Calculate the expected frequencies of mutations db <- expectedMutations(db, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, regionDefinition=regionDefinition, targetingModel=targetingModel, mutationDefinition=mutationDefinition, nproc=0, cloneColumn=cloneColumn, juncLengthColumn=juncLengthColumn) } else { message(paste0("calcBaseline will use existing observed and expected mutations, in the fields: ", paste0(observedColumns, sep="", collapse=", ")," and ", paste0(expectedColumns, sep="", collapse=", "))) } # Calculate PDFs for each sequence # Print status to console if not using extended regions definitions cat("Calculating BASELINe probability density functions...\n") # Number of sequences (used in foreach) totalNumbOfSequences <- nrow(db) # The column indexes of the mu_count_ and mu_expected_ cols_observed <- grep( paste0("mu_count_"), colnames(db) ) cols_expected <- grep( paste0("mu_expected_"), colnames(db) ) # Exporting additional environment variables and functions needed to run foreach if ( nproc>1 ) { parallel::clusterExport( cluster, list('cols_observed', 'cols_expected','calcBaselineHelper'), envir=environment() ) registerDoParallel(cluster,cores=nproc) } list_pdfs <- list() list_numbOfSeqs <- list() list_k <- list() list_n <- list() list_p <- list() if (is.null(regionDefinition)) { regions <- makeNullRegionDefinition()@regions } else { regions <- regionDefinition@regions } # For every region (e.g. CDR, FWR etc.) for (region in regions) { # Foreach returns a list of PDFs list_region_pdfs <- foreach(idx=iterators::icount(totalNumbOfSequences)) %dopar% { rd <- regionDefinition if (regionDefinitionName %in% c("IMGT_VDJ_BY_REGIONS","IMGT_VDJ")) { ## Prepare extended region definition rd <- setRegionBoundaries(juncLength = db[[juncLengthColumn]][idx], sequenceImgt = db[[sequenceColumn]][idx], regionDefinition=regionDefinition) } calcBaselineHelper( observed = db[cols_observed][idx,], expected = db[cols_expected][idx,], region = region, testStatistic = testStatistic, regionDefinition = rd ) } # Count the number of non NA PDFs list_numbOfSeqs[[region]] <- rep(1,totalNumbOfSequences) #is.na(list_region_pdfs)] <- 0 # Convert the list of the region's PDFs into a matrix mat_pdfs_binom <- do.call( rbind, lapply( list_region_pdfs, function(x) { length(x) <- 4004 return(x) } ) ) #cat(class(mat_pdfs_binom), "\n") # for debugging #cat(dim(mat_pdfs_binom), "\n") # for debugging # IMPORTANT: if input has a single sequence, mat_pdfs_binom (1-row) gets coerced # into a numeric vector without matrix(..., nrow=nrow(mat_pdfs_binom)) list_pdfs[[region]] <- matrix(mat_pdfs_binom[, 1:4001], nrow=nrow(mat_pdfs_binom)) #cat(class(list_pdfs[[region]]), "\n") # for debugging stopifnot(is(list_pdfs[[region]], "matrix")) list_k[[region]] <- mat_pdfs_binom[, 4002] list_n[[region]] <- mat_pdfs_binom[, 4003] list_p[[region]] <- mat_pdfs_binom[, 4004] list_numbOfSeqs[[region]][is.na(list_k[[region]])] <- 0 } # WIP - check Milca's funcittion, hotw it handles hte new data struct for rextender regions # Template for values for the regions mat_template <- matrix( NA, ncol=length(regions), nrow=totalNumbOfSequences, dimnames=list(1:totalNumbOfSequences, regions) ) # numbOfSeqs # This holds the number of non NA sequences numbOfSeqs <- mat_template for(region in regions){ numbOfSeqs[,region] <- list_numbOfSeqs[[region]] } # binomK # This holds the number of exact successin in the binomial trials binomK <- mat_template for(region in regions){ binomK[,region] <- list_k[[region]] } # binomN # This holds the total numbers trials in the binomial binomN <- mat_template for(region in regions){ binomN[,region] <- list_n[[region]] } # binomP # This holds the prob of successin in the binomial trials binomP <- mat_template for(region in regions){ binomP[,region] <- list_p[[region]] } # If regionDefinition include CDR3 and FWR4 - then the regionDefinition is different for each clone, # In this case - regionDefinition will be NULL. # Create a Baseline object with the above results to return baseline <- createBaseline(description="", db=as.data.frame(db) %>% arrange(!!rlang::sym("tmp_baseline_row_id")) %>% select(-!!rlang::sym("tmp_baseline_row_id")), regionDefinition=regionDefinition, testStatistic=testStatistic, regions=regions, numbOfSeqs=numbOfSeqs, binomK=binomK, binomN=binomN, binomP=binomP, pdfs=list_pdfs ) # Calculate BASELINe stats and update slot if (calcStats==TRUE) { baseline <- summarizeBaseline(baseline) } # Stop cluster if (nproc > 1) { parallel::stopCluster(cluster) } return(baseline) } shazam/R/sysdata.rda0000644000176200001440000014330314142206203014074 0ustar liggesusersBZh91AY&SYtEE |;rX3" AC^M} WebRٚmqp&JѶ]tHU6 @D} @ d `l4-ccG Yn@N @M! 2)U(5fKfp8, {8.}zdoyK>E.q){n$}7=Bvr\@w=UyO;}}|p@>=(` @0.}=(<a@"$"@DAT=e$@6T2h44 0i0& lM2`&L022dɓF4&0  24 I&&PѓAA4@@@@I"6264=@&&4@hhW~SRS)@2@h4 h44TL7BTH6F h4A2bh6FP4QѠ=FF4?Q4h "MD&djjzh 4Aѡ?lC$@D}͡?k!кR@hә-6Sc-"$@*iHP]dWc k=]۷vVi+NX5jkI3a=c\UyTAlZluhyaYeٻ|FVJ-;# OŤFOf۩b9M~oc޷9o5\7>3Ӎ<gM,ei>b#|VLkSޯi,U*nC/uWlgyNvy\u(\z(ՎtYz^$o=pL xN_b"qsܤ^xMΥUʭUnT>[r5浬2-yǛdYM_W}u^im(m%Gqw`gNNZد6 j{$;N\okEdu(Wjownp76.D{,]yR7\.OƂ]#fs [ >|˭lq8lŕpާǥK?Ғwt{/2|ƞ^שּׂ:K#|,ڑ}c(]k5:[U8BWIBOInͫb+es3$Fki%8E\K"Nmd$sޞ,z͍"5^u]XXKQK4Sc9<:tp|OWCzcyU<{)0_Pmx omr>Ss]2ر;G43=Pi/,JN-Hq>^Urd2 )nPi; [(PaHG|%KsTƢFT-?P_)S$ܰ:#~MO5x{CĶyN8InP+# PcOīBa_JyZzU}w {oɣΝy0b1g|/yLɖmgTO7JSC] ݭWr!xOdW^mL}߸aC߽r(䫝!6 +נ֧W Vo^B=HT7wGӸ鬾K& y0G Qym-jq$R*+:y^=y9s1zոF:fo=SU9i& ݬ m[i82?oIݕmGRYؾyר4!2W]0߽6S CW9{˸U(<y_S9}1C| a4nB-b7kN6v1>սq^i1┤e[GZy${j8=FOd=Ǖ^zz5vݧy?b'FirL4GFۺsHwkwy45ƑC^\"_qIz3;8TkxBNZ!8䦯Gh*^Z?qǮ6 ˷q+ˮC+=u)g ymyMB=]R'>#T96~wYrׯ{WAϵyX^ԯoWۄ{$/ˍ\GykեngϠش' 5;Sy) eVe}Ku0;dѶ:4ciѣBd̔1&B$D$L?sᆬ)0ըS߱0ӳ'&CV4y A!.r?cܱqz8^c-g''򿬑snyg(]s͓|:m>bS.#֒()+Y"&Kol\KіR5Z 1ωލDU% I_EZu@9cM& H) 8^RiiZŋ#vzpQ"4D|tx5Cf(Q} WD1i,{4 % BڦnK;/|#%ВYf6NQ웝L\l&"^ɮգc[KfMh}w^.#Ii\ǞF Mk*Q{YibcZ<^WsmH#/HMi))&8zjkn6+Ch҅HiN3Sϣa1jg5 5@qtXk1z"9i**%%<3n5)QӜ3^ҼiZ719ST޲riKƷf=#N&\o)) 7 U3?Yxûj;ۖou RV[ֵܺkw-jRZv3$mX$3Q^g}Vr1)Hʼ̗] Wz+WɮzYo3\' TR kR$'+&6-r8 |鴼8Z##RWz1iisdq%󧓠)?;fٓWs}sO=nIveRPNȝj.w%*O<۰q}O67!oGecjd-IN9sZѫs+ۭ%b-FZsiVWjd4Ƹ;H--kn~{庵g^^ty5NtxUY' %vŹԛ+ɿƽLq'QHiVA4;;XۿAYjUdiAYShF$֨;5խ>iuS|<5ZяUW'{Smd^[;Zs/%UShDS )Tߋ p-#ޓnOܸr+Mvxrdﻥ+h9\U> pBa2}#XǤw-N\yklmܩt'=Y %s2T[98|4~]tœ5vձX3wWꛎ{9i~4rZAYF}&iukbSH:+KcYџsnKb3볶Ӈ/u{qݞمl9Fٿ`طfzͼίy3i,N)=}m~L-tT]]OW\s4SӨy](Pg]>Vΰ|~0 !@~4)hyq4 !ݤH V֘qӴSK*7Sp_ei]m[:u~gwČD$DTȨ~?` >?Jݩr_A_ˆTkJ(v(wtj!Ơ"OĦg+= 0}@R ( c 2CQR1NNTTp/6@equҴঢ\ eYϽw|/s9Ϝ$[t9ۯ7]xun7Su$[t9ۯ7]xtG n7Hmn nwYmӛ-ۖnwYmӛ-ۖڍun7Hmnܶmк  z]ns֮^WBt0t*t鎕vwj۽ݫ׮z۽ݩu략vwjiz]ښimu9ǜjv!-n8sUlmx6n9sWvlvxmڪmUPmmU]vmsUlgp-9jmNmTmUW\gmӚzG mymFڍmUAU;j6mUmTmmFڊns97xD{6mmmUUUU mmUURj mmmmmvmUmT+յYզZ֎uZ)()(^Q]vh())mmFmmmmmmm(**(mmFڍڍ4UUUmjjJ6j6mj6j6UQTRmmSEQCFښhmSTRQ= EymmEmj(6j6QE QMQCTPQQ ((mQE5EEj(()Jh()J(J=ca@ YA,hIY, ۋ6p6D{q7#s7Cu7ca-ςjfhN|#y7b7|7~7/&pF$3l' lp6#g6c88i8>!aiypg$rg(r,|c>9 Nh'6s:p ?l(*b: sQ5 Xpt%+_ޱ633;{{{{v#9m%?A%$@If@k]**?)W7劵UҮ{_-]_?w嫫f;F;=o>w˽f;F=oNw˽f;F̰d)e$3, C_sO<}9(9bI%I!dDȒ.&&I [$d|6I<*& !xTO C$>F⇆x$@|3hȡݮz=p>{Ϧ3%2393%2qΌg99ub\)>BD4TU$f#!"w 黨B9p@]nfA )@&H!<, MB.( Ӟ7P ԨE@ />|K@EXň X[;VB2G{wXB^aya =a˽u(x=Z=;!GXhw $̬$ID |?';uxθ T (PYYY{ndB\")B\#.ʢw$DIJQӾO<'<U$IUI URC*!T<}wǿnݹk|{:ut׸"|}&~IhntD$&$*$ӑPoMP۽tswo>T8fq"393Y7dQ 7s4^|뻚/Nwd&sXN/sXN/sY@+w͌~%"D!= Nu }{=){Ǻ>]w{ٖ yts9s9<^go/8妊f%IB̀G* UҮzyz*JUҮz>EGW7VW{Fdd!BM "3srnY˙zdnk2N3R)B\#.ʢP2DbP#*K9rR(3RΙd3rnn\ϧõufc _7+ܮܮARjɏ9į\7+ܮr ^2LxτY[nWy]^$'[&=sL7+ܮrϧӷ~}HhBN%uo}pİ@Ve$]UI URC*!T̰d(Nu!!mj!lJHuU$]UI URC*!T)O jqm [d5mU'_r?<}9 CDs${MwI%.RHbdYI7$x$.MgВokI9/;$/;$ &~oW/wnУݮhBdG*n2%PE&N#"FD"E Q$cxf{>%#nm@+q@]hc"5e˄9J8F@H+6F dQP.2A8tfAlp'[Wqk ~|y}xD$F*xDrr|\_\1,!2 ̰ٔd)C6k&2u֫jkzu:kZy{{;G{ Lٖ b̤ve!H})=B{k89UD EI8C߹$H`$?\(d2u֜wvh]m=mp>{׮2S <?/V  9B\70PMBˆ 9w% .T}j@\聢 ~|ߋ&sQLkTuZ={uwu({uc22 H22>|^9s?ng>|뎜q2|xA{ND"! T"I"9S4HQ;! *@1Z1F@H+y|˄O<)|, `f̰d)Fe>ētDQ$QMI7d <37"3ED5Ϡ^1'KP0=z ӇCwKb|@w !!fRX2H xuB^u sîk^8ֽw{]=zs{};{M*Z]*W{G* U`If@I ! 46Dbr\˙f77.ed̓Գ2̗3srnY˙^r@'[f>+Vw^ 5dǜWY[nWy]翿t !!|OϜX[;VBmYlj!l5QLIεd:d-CV [d5mϙ/gx뮳=d@Bs]HzO{]Ij$/I5{]$bs@@ nBD(Dr葊3YӐPnk2\|^9s@E:@IU !PI!=-0 +EK BTF`(-fJdu$sps( Ӈz n`)/bCR.}4ʆB={u{?G{g@}]ɒ__$78ΐ)2HK!bdBHz;,{MD wv|ӎ+9rlffӭӦk3Zן}q~K IPK"dC:\2+8}PLRO jkY߿_.ޞysss-qӎ5\qs|Z]*W{򽑪LTG.UʗDrQyKy|sqs3Du$x .{u z^ak 9㌃k3۟@c :\2ۄ81F@H+wa\"yh/!AlB4fRC;2<>53O|:t÷n4ӡ :t/I_X}TDc!4΄A/C࢟J/?P4ET4IQ0MIQMQ4AE3$ EUQMPДIU-SM4PLDE MUAAE3UMLQQBAQLIJI@$QPJSE%-+0TT3(UP$*@0 /@wLkh`Аgƀ6dmY)ڂmcc:`Í1kZڈMT541PDEDVhfBT,IL2j΍0F`MY VH(&`֭ckFub;A#TcgFB,FCqM4mZɍFAΧ;F#9DniEVM&X"6bMhƳm%kAtiN*mk19DT1mgFS[[VEQbHΛi1AA)4$!-63LDljtQkcqϵ?"P" |?( @~P|?YDР@ԉfӵ=A Zd{A!2H$Wyh}~ճI H n,{I>>ĆFAAj12j2E#Ɋ(&&RZ&J96E4PUS4E%t-4!Hh h1#jqSA113b&&F B[И#لH*DCih)fbb)"i $dHi,HLQTT35CQDDPA!IRPDAM RC$@AD)ēT)PBA+ ФĤR, C%Q MACMR~G}T:/&zàmW`>^ ɸjُ#!ՖkxO@$I! .:OȤ2ߺ@~wϖ orPH I4@ fP@|$ )"?_!@۔GTM4LL[f: V T s`uQ1 0dh 2A)Pr 2VbB-F(dej-vؐ)vBUR[-[4ioU?g]dq~U{5p>c<@ Z,pѿ^&_-e>,rfcqؗhioG] 혊( iJ!"IK3DAM ҏ jbP8?<yCB4*Gᶖ *H&&)H)(!@x Լ=WǻOf$^Ac @HdƈH? W{A\B0#(!Fp駆: !W?ס |Rc1_T&1\ID?U,OҼ1 H!b!h_NqeaVVVVVZP!eg|6|nn *,027!8`Eo6l\ aEΫzvS_ 7~s>_}d~2TTDIm "*( J( A)iEw:?PT4IT LHAE}$-Zb!J &-gHsފ}?Q~+nSwh#n/]<-&_7/a*?-Ar3XpyW];\ΟEr찛ֹĺpi6SʽX NFi:GȷdI8/[{e45l{Wv~ב',G߼\Or;pj69?Xy\~ cdu/G|w3r%eޤ-ȹ3p_Kk?n=A}d!退| yΒZ^7$  9(tC!`;>óqz3X?~+ΊQ%^sՊWpp~2A?_lyIsl.N<5^kP?e &T1ȭB0 ?8=DeRǷ$:;tVB XpvL<[3%~7"I+} }Z^ cοe2@ xf#D)0-2 cSK:ӯeijFݾ1?" V8soΘPe!Nˆ1?°k à ܔ|oj? slw\w_Y2uᬞb[Fc0%r C:t`O )][@ @tԘlPm4G7kvޝdņd61Pm>Ђ`иA$|t [$ zFcQayEM(O쨵&'8d ,a Rqz"ƒQ4 EkBb hՐj'f3`??=xRZ&>zl_Ged;F/>@@]?R>`~ꈡY-y2fGo@'3 7j0ꗓ HdA PF!=Z#K0++p)@{ᱭf>fhvZ*=U"y [7ԭpn\sF0&g^U &&8 2W3~?xԀ h@ Aб0T O D4CӞxo$J|~]b8Uk XT15gJJA 0N6!aB'5!H3K·) Ifʹkw(1$A$n0XqN1jkMvEKx/V?^uSsqp ë.Z؎òFU^G xs|H=7?\Oʅ>u͑a9V@+&/yLQ2$jXa"d283ɞ=ng{[< Y[[V%SvGHXwHʃyM<|ϓgC|]:#N"-3 {+ 9g45F;Xown^׵zt^0_X(  PH<0@Ȑ l\( v~N#NocZZk_̚Z3.b/ZE.djD1I,\o>򋅧^2絥[N !z}b!}#$bB>إ%츜f 7mW(33p2x7W4A~VaӮdz{-qhSϘxۭ^KdTӴ2˞D>^\rOHKhF"@GZqZ0+?K%BƤ\7D;DdVICXJqfγxsq7%ƃ.[YYjV|,KawNkXyTG&K5(Ԉ}LOG HqE+=&WkIFn2WώVDA,9)aH${~VXhYE ӿT5_yLQ(7ۤuEe!jlK3|XUrNW>+;fdvZ"yɳ ,l|6մ%`G%>\Sʏ?0kv΍@|ŵp͊|7ǜeNUGs|&/˳PT">YfUA/϶n3!!7]&Y1>Ԟi0>ܰ)>L |lLQϫ4@ $[q>Kp5xLRH"/ϔĘt %l-j{g{TTWMJעv8zLePя# ժo0<) z?qBK)bH>z@,@$8DR8H}M޷:j?Ľ0- D,cn5q"CRyuK6sb .@b`yWҩ/ҹ #IB*l8)%>l {t('Bp'Xm?RUz,kaqa\-ׄ_gԥ^~ul5kN8M2MM?Y LVi- C(,-)>ÕDPW2aQ,*LJDM+H}(1 IXr4bn Iz(a V<ܮaao,HA B\R?#uu jV )A$;) rڽ5=79($1b1PNزYV/:z0HWOlK)b&xSǂW2tg }MM1}b#[s}xND,xvkj떎+42}qb`hU>Nd,"/WWRB֚~\xtpd=`9Xⱃ')QmI7͐Gd?T%GꑑIYXgq I%'fSc*{G-=zԀ:( %6&jC%]4~@~f)ܞpps#0Y)`O?xAD=s#H31/zMN$UR\2%g&Q4I@Kp4luę늵(h̥1 -2sp̗uXEQ"y$QTMg&V(E438dΝPk3 HlyL<ĺ"Ӳě;}X&$a0! :OI۷Mz/Xp8з_sOCT*|9uyBe85ACh%'j"K%;NE|wsFVi˶%DE ՚j+D8 YAQ6C!@!]a= ƊXBvgMP4X8kU|^EW4cOVt (u0E =*Ke4*"WmѻQ ڔTҲ3Y's HφN'\'9O}?r!0rCy[º1F&&&폟w+ Z"9%~MIe ufo~IN[52&Б)Q r؈dQf֭Pܢ b!/!6—*t@iô"yNܞ3xۏȈ2'Olp>H 4wO~m(j{!hʥ97y8QVآK%/d?[{"۸\yg(/8!kXJ(J16PyDz'죻VZ)dn?pB#@P2㗯Y>l|0BC1TU&; !tQ`&3sG'Q^unဥ8T}}PFP䤖is @KJJPuYۣ8WMSמm AS =t4?'4ս߇nMQ9~KϳJUy|o!zx8-/nx؝oo;%lҕ9l}n۫ƍu?herW^4Η='+rq|ޗgIj5Pk/+ŏl/\̍Qa1]>dh{ǯq۝?7_t#C@}btJS'NK8; DL1=e$3 |s`)ͥ!IeI|=?hģa^O i𔭫70Wxg~838+6nt6/FӥЉlXb^{v뿤! -,-3JM\,(1}VT9IHiuiW]╋sn`Ic')+D@GA! C`߭~} >빏jhP㣋Mr&8~۫_aGqe[YE \?rFePPvʫNt`H]L#h7rR)J*R]!k~0W#$.;Rrn8I$ R$xB9I_8of:?$$$$Tmkm(0ŵpjә4Qi3g<@KnR㗮J<@rBQ֎ϨrIP": G `$  _x~[O1ҕc-ڬa!/X_kDT$R >׍G3‰g6zhM4/J6%+%6jT$wGWpZF3DMJ9\dbe ҦVf[y6laC]PxLGG} 翑4$O̥ 4$]h2@D3gޟ_,~t0HIvZ`,*$:5sY0/Q9Xospv.2a B-~G2FN\g[8Q:H%M"U徖91gYp% 'p񤑺 GI^3fDqz\Rf+܍s'EjA hcaTL[=Sܟ >iN\-kH0HA)Pt/`N0$X߶(nǝ0ƫ*R6RDRWzml? @$ >  bh0(9rP ҍwb/?ԺWp4j%wy ;5| Oy#'),fzIE'n|Nä4tĜH҇ w = 00! ,gS+ruo~':q5px3pN8oȫQ;31/etޒxp*>cP"]N͛p;@{‡+I嫬xULa⧎k*Կ";<5ʜ,d%^8xtv 0Yi5k:n{Bs+TԚBj񫺵YfD9RL>j'$>f:ZCO AB h䘙I$>_n L>dq|t~&s!F8mN=Dhs;bgh!b3ݙXqP#H4W'Āf)B R~bTx2( !5LZ/А#+ Ԕc@UѪN,ngF0FRK *pVIh\L$bFEB  >"GJДؓBr00G aŽsg.D < (U)D0|` [ B7ud=%r4XqDx=)AVE1@wܶjr߁U_{׵~ø*QbW*exC8d L*`Y-͕wZI5L.iȇѲ$ 0 @- *R^l̤BHCg#J!$(@@0|Y_._o{_ҼFAʤ9 Tm٘w1Q<4+CdTL#GK+J Ifd5jRDKf8zg1x=,E-cF8u ,1*" I1$L)!JrJbV`&W$BX (9ï_jq|b!QO|!۠!DH!*#`>{Ua2=KX4ߪ08oV"?_jQxk!'5ϫ5cZTQK:nw*YyF IMIF,(D,rĆg PF bT"V"T"th_3f[g#ibI"@E.dd 3$ ~b>=0Dnla[ N^L%w8Onߜ5(0E|v#pw5BcIzKjwR2rzZh7QY~DCY Z|DLAs)A@= f$Ò`{ѝ'CB" ~ D9N|H2=\VhHn W~ ϾMk$-!j!|ûQNEhR aG˘7/*)sMB-sFEe*%ޓ1ja/h: _*&jT CXF#)#'w{oˮ#wS!Էi H<Fq?N>珥v# fl~VڔuW(bNT&ɚ_/JI\^OT6e۪-9b\I@"T"cD0?F)!;\>"2(zEOy[gւ:M$uD.'f dLGOQc9^Y,PQ _mg@"|U ;Uh"G9>:(:f, FνquX~ vA?8 D|I$ Vj8@ JsWor ԡs:SgUn3TA~3H QU#>RmLM@2:Z )D&;<*JJ m5 w$wZaBtnN(.jMIJ@2~%rm/T5.6G P|x11{BH@Ló|L31Cd@}$HyhEڔyUTW}ᮛx2#$+# S^з! ~|$CyvED[+O?q'*G#z6:QǙIX1+R*|)ec(h>}1^N]{I&$h(>86&dp Uri$`=c(-fלyp;"EI@4|&xXH0t>J,Ha1{w` @@c(HDQ_WKM"ab{Zzy@IǣXÏA NP?cCfέq,>^#IBdIbw.{y~f󸃎;QOb>=ùX&CKKPb̈́ds9Ac !0R:$ Ea W(rt< FJa 7 1XhBx`>p&V {߾]b4A Mx`$a LW0=Z,`L0$BaB>Ce `&a`Lчч֐p@?HDP?o/#㿗35UUUU]|}|< 1nwsuGnM~LsP1"Q3ciECg86ӘmDqm1ډNb*91T>_#{@kZ֙8uι:ƳuιXkX)HB!!!d-Bd-Gv`J$p8s9 c167z`sP0B"am'1 CN333U^_'@DӧL`Ҕ!a-TJ EhX W uA^qldLL.˅;Ԙ$HXYeuD_==}}h1Dș ˖s"DH,,X$I$I$뮺I$I$N$I$I$뮺I$I$N$I$I$ϗ89Îffu]u]u]u]a$ $BI!$Îffy$I$I$IᙙfffffaI$I$I$330333 8I$I$I$330۵v۷n۷nݻWTMvI$I$I'ffafffx<asmH gvfTLS#s$$Fv4)TJ EhX xu2&D& r)&"D!aeU*RU2u"$Dbߞy912&Dpr0DX,XTA߾ݾ $I$I$I$I$I$I$I$I$I$I$I$I$I$I$}9I$I$I$ffafff8q<$I$I$I'ffafff<I$I$I$ffafffpq<$I$I$I'ffav۵v۷n۷nݻW>>~?Y뮷CYfP"Xʁ2*"#pvQ*%DDhX,VX_u:r&D& ppzD̉"B,TEHu:Ȓ$,,>U>֌c(g9"dL. .Ỷ"A`bS~vtӦI$I$I$I$I$I$I$I$I$I$I$I$I$I$I8ffI$I$I$O333 330̒I$I$I$tfffffa{qyI$I$I$333 330|q|> $I$I$I333 330=<I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$8yI$I$I$3303338yI$I$I$330333 9yI$I$I$333 330< (PB !H@A0kZֵkZ*R)JUUfeUUUVfUUUUfeUUUVfyjo{{̪ʪ̪ʪ̪ʪ̪|~ۗKw{{6Lwڻwڻhu׎yy^smgjg9͵cj":G9rv5j(Ͷmͪ(:;ݩ֊ymUAU;j(6j6m(}kٿy8ZֱZֱZֱZֱZֱZֱJRJRJRJRJRJRJRTG9Üp8s9c1`0c1/zRLDRIE$IU$RLDRIE$IU$RLDRIE$IU$RLDRI333333/|/j&sbs&ڍ919mFډImDqNb$Qg8'1mQ3c6m1:g\fZz/ Z֙ZֱZֱZֱJRJRJRJRMBIBHRPG9Üp1 cwv Nb$MmfIDRIE$IU$l*'YÇ$I$I$Iafffffafffffax<q~^ݻv۷nݻv۷˳23*23<~3[~{UUUYUUUUUUUYUUUUk3yyyw]{{fUUUUfeUUUVfUUUUfg)ڔ)Dh 85?X$|ոRGc?m߿J~?l~U~ߝ'~kUUUUU\֖GIZa`h֭9E:wtCcw\:p1wwvs9Ͷڳv&n8sV6EЃ9s]#jmvtv3Ͷm 6Bz9뱶9f0]9Gv(Ej(mmڪvmڪ6jSs:;wimFmmڍUQTڍڊ6QTj(QESE4j(($xp79{w5k5k5k5k531Z0( `('3Q3f|xDGnlQ 9968cۛpCc93g\jLqk/ k5\kすuDŽ֌c.58Ιt3Ӛy!ьJ ypq!hj"ruDžSǏ7׎WQۛpWc\^?|>$I$I$I$I$I$I$I]u$I$I$I$I$I$I$I$I$I$I$I2I2I$I$I$u]I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$u]I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$u]I$I$I$III$I$I$I$I$I$I$I$I$I$I$uRI$I$I$I$I$I$I$>cO~'t0Co?JR! ""!&vQAAyGцp @)JR+Z0iJR-BkX)H@ Q*'$HD8<0m!0m0a1ޑ40A3S1"H)Q ")J$ߪ+lQD0186qm&DȐDNÇ~Ǟy{{kk[{ʪ̪ʪ+y{{̪ʪ̪R)JRKGə8K_L|n;vphgghaPǏCCBmmmm}>ݠ@ @TMVkZֵkZ*R)JUUfeUUUVfUUUUfeUUUVfUUUUfgUWUUYUUUU{{ʪ̪ʪ̪ʪӯGol96 qwg~GLI7&LY2@K]߿~߿UUUZ\fUUUYUUUUUUUZ}o8ώyyyw{{3*23*23*23*)JR*NJ0|Ճ+peYeYeYeYe0^ntmFڊu략vwjimUPmNڊ6j6l C] wN7{q/|O_zzHDBB"D'g9s0D$H@ؠ6(-ǏO}Kוq RU T) kTLsXф<{)B"mBŰP֧5* 5S3ϤӮzz8ʯN=8Sz 94DQL kZW\guMk5/'=c<3<3<3<3<I$I$I$I$I$I$I$I$I$I$I$I$I$I:뮤I$I$I$$$I$I$I$I$I$I$I$I$I$I$N$I$I$I$I$I$I$I&I&I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$ɸkϹ>~/>_'՜f8p9roQ:z8 $a L!'o^w}OI$I$I$I$I$I$I$I$I$I$I']uԒI$I$I$8$I$I$I 330333s<cE'|79oy>I$I$I$I$I$I$I$I$I$I$I$uRI$I$I$OqI$I$I$330333s<L#"|f0EKS3?{{ǧkmI;n^:tӧN:dI$I$I$I$I$I$I$I$I$u]I$I$I$I>̒I$I$I$tfffffa{qyI$I$I$333 3308<$I$I$IᙙfffffaqyI$I$I$333 330<Iֳ7ӍLPC\#E&jff*Z`f &jff*Z`f~o_^Q汶؊"mFsb* km"(6g9m"Q汶؊"mFsb*  (I 9s9xDC᠙& @&_dpX0̀m33fp* ;L0EKS3;lg bLDžO<הpxۆb*!P`^s9ۆb*csg9m"9m1 =xY̸8kY3ffd,P0(BPFA&AfqKfgǏ==60_7}I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$뮺I$I$I$ddI$I$I$I$I$I$I$I$I$I$I:뮤I$I$I$I$I$I$I$$$I$I$I$I$I$I$N$I$I$I$I$I$I$I$I$I$I$III$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$/||||tW1p1smT'mE414!F0`5k4hѣB  )k4)!R`֔ [lBD0pp98p80`1cN<c-!aI*aD1`J$ {zќ汶؊"BC!8p186qm23 0A3S1C>O7}_c$I$I$I$I$I$I$I$I$I$I$I$I$I$I$O8q$$I$I$Ifffffa>\pI$I$I$330333 9yI$I$I$333 330<o=x9cm>\s>Of}OI$I$I$I$I$I$I$I$I$I$I$I$I$I$I>$$I$I$IFffafffyI$I$I$O 330333s<I$I$N$I$I$뮺I$I$N$I$I$뮺I$I$H}s33$I$I$uSffafff8q<$I$I$uS330333 ߪ|>$I$I$뮺330333 8{{fUUUUfeUUUVfUUUUfeUUUVf|fND&}>LX;B2e$ɓ&L2dɒmm7z۷nݻv۷nݻ|3*23*2UUUYUUUUUUUYUUUUUUUYUUUUUUUYUUUUUUUYUUUUUUUY>'~/UHiJM-i4JQ44S@fHmHH Lq4Waѩj mr @#BRUADSLKSv R90m&SA @š hLM:N!NN$4tmLQD,R+0 D%%% P  x%s@7p?sֆ$Moۚj|D"KL,Rl>a83L,L9?(Qf1VW)v{<s hvX0 )?\,fG~K pH%J@ '3ف "JH( !HG}?^ֲ/šț\/"t]sOb\7K/"L,Վ/)Fxf^r~76-@.鱗k4)V-k5{DS%>(b "b屶1DL0%TCB)HУ@Ӷt`**CkqDEDLT̓$g $M)% JP1QLM.кݿ͓upnו]{zywTy66~cb}\K]3KQ#za`2<bx݊ fUbmݩ 0/췎_tK+I@jX a0P 6mFΝ&6j'M RBAЎ*f 1͍ "P% G]JQQ$ x WĆA@̤@OdYkBht44!V"PHm`I Fv8CoN~Xח}> ԭej &!RZI$I'0`ŎIgI,%s`*Mj $ &(M:hBa$FuJTHUA$A@PM5KTҁM)@P"<osbkN@AV h& ,6FƒF8$*X $djhYHB$Xf73d"v%ii" KN6vѭeC^QOft~Z |zEimTϱ_ׇVȀU Ly=7 }|xw̠j&+9b_Z%=VY,:;]L@(%Z b# $ATLLC},P~{}O)Hi QbBI{f`9&kٓEED~3 zcLT[vIW4|?dsEK ǻǫz۶zS @~*fLUjS-OҽOkO ް//G:]Ӧ]Ҿ.+5j7G_Ƃz۫|mQ$?z"zf***A35L44 ')qd]*yɠq!4P,ʡHnsOfDH%I"@DP}D}NQ|A?G5.lp#wb{J1@iFiDW-v]Ι4٠fP@Sg\H"y$风3:^-  Sl @@p 8$H?cAM~ Z j=@' pH>E/~lp$XŸ s?o~~QT!PTS1TTTUP% ң2(i_P !B4Lyeӆ.Y)V $Qs8RfY LG wcت) N" uP?v CkrLH#c̤M  *}eTSG > qUT`SKOǬ"(d ~܈iA?QCЀ!CT4|@ yHMRp@ mh@"$/_ƒ Ώ}4 nAʠ`+I9Cd>E/@~AIG%$Z(46#Pi**]"iP}_]OuT1JQ$M AA  CT>| BP lF!)j`(CPEP?'@ Dш }0n(@tȚlRG%ƕua0]U(Ge&i$'@t]9x%1h) m,u<$-l996_< {7XEQ~_Ɗ(((((()UjUX~)@.p mshazam/R/Core.R0000644000176200001440000000245713670240241012760 0ustar liggesusers#### Transformation functions #### # Converts a matrix to a vector # # \code{clearConsole} clears the console. # # @examples # # Generate a sampel mutations_array # sample_matrix <- matrix(sample(20,4),nrow=2, dimnames=list( c("cdr","fwr"), c("r","s") )) # collapseMatrixToVector(sample_matrix) # collapseMatrixToVector <- function(mat, byrow = FALSE){ # Get the row and column names rnames <- rownames(mat) cnames <- colnames(mat) if (is.null(rnames)) { rnames <- paste0("Row", 1:nrow(mat)) } if (is.null(cnames)) { cnames <- paste0("Column", 1:ncol(mat)) } # Combine the row and columns names cobminedNames <- outer(rnames, cnames, paste, sep = "_") # Collapse the matrix to a vector if (byrow) { collapsed_mat <- c(t(mat)) names(collapsed_mat) <- c(t(cobminedNames)) } else{ collapsed_mat <- c(mat) names(collapsed_mat) <- c(cobminedNames) } return(collapsed_mat) } # Convert columns to uppercase # # @param data data.frame to modify. # @param columns vector of column names to transform to uppercase. # @return The input data.frame with all entries in \code{columns} transformed # to uppercase. toupperColumns <- function(data, columns) { data <- mutate_at(data, columns, toupper) return(data) } shazam/R/ConvertNumbering.R0000644000176200001440000000350514142206203015345 0ustar liggesusers#### Convert Numbering #### #' convertNumbering: IMGT-Kabat number conversion #' #' Converts numbering systems like Kabat or IMGT using these conventions: #' http://www.imgt.org/IMGTScientificChart/Numbering/IMGT-Kabat_part1.html #' with Gaps (unoccupied positions) shown by "G" and Asterisks (*) shown by "S": #' arbitrary mappings (multiple possible "to" values) represented with "NA" #' #' @param locus string indicating heavy ("IGH") or light chains ("IGK" or "IGL) #' @param from string indicating numbering system to convert to ("IMGT" or "KABAT") #' @param to string indicating original numbering system ("IMGT" or "KABAT") #' @param calls vector of strings representing original numbering #' @return A vector of string indicating the corresponding numbering #' #' @examples #' convertNumbering("IGH", "IMGT", "KABAT", c("51", "23", "110")) #' convertNumbering("IGH", "KABAT", "IMGT", c("51", "23", "G")) #' @export convertNumbering <- function(locus, from, to, calls) { # Generate mapping from references from_map <- pull(CONVERT_NUM_REF, paste(locus, from, sep='_')) to_map <- pull(CONVERT_NUM_REF, paste(locus, to, sep='_')) # Check for compatibility of reference with input calls if (!all(calls %in% from_map)) { stop(paste("Formatting of following characters does not match reference: ", toString(calls[!calls %in% from_map]))) } out_calls <- dplyr::recode(as.character(calls), !!! setNames(to_map, from_map)) # Separate check for ambiguous values to convert to NA, not arbitrary call from_counts <- table(from_map) # Generate output for(i in calls){ if (from_counts[i] > 1) { out_calls[which(calls==i)] <- "NA" } } return(as.character(out_calls)) } shazam/R/RegionsExtend.R0000644000176200001440000007371514377106452014665 0ustar liggesusers# Region definition extension for including FWR2-4 and CDR2-3 #' @include Shazam.R NULL #### Extend region definition to CDR3 and FWR4 #### #' Build a data.frame from a ChangeoClone and an igraph object containing a clonal lineage #' #' \code{makeGraphDf} creates a data.frame from a \link[alakazam]{ChangeoClone} and an #' igraph \code{graph} object containing a B cell lineage tree and associated sequence data. #' The data.frame contains the original fields and additions such as each sequence's parent in the #' lineage tree, the lineage germline, and additional rows for inferred sequences. #' #' @param curCloneGraph an igraph \code{graph} object for the lineage tree generated by #' \link[alakazam]{buildPhylipLineage}. Note that the field containing the #' nucleotide sequence in the object must be named \code{sequence}. #' @param curCloneObj \link[alakazam]{ChangeoClone} object used to generate the lineage. #' @param objSeqId name of the sequence identifier field in \code{curCloneObj}. #' @param objSeq name of the nucleotide sequence field in \code{curCloneObj}. #' #' @return A \code{data.frame} with sequence and lineage information, including the #' the parent nucleotide sequence in the lineage tree(\code{parent_sequence}), #' an internal parent identifier (\code{parent}), and additional rows for germline #' sequence and inferred intermediate sequences. #' #' Values in the \code{sequence_id} field are renamed to numeric values, #' prefixed with the clonal grouping identifier and labeled as either \code{"Inferred"} #' or \code{"Germline"} if they are not an observed sequence. For example, for a lineage #' with \code{clone_id = 34} the new identifiers would be of the form: #' \code{"34_Germline"}, \code{"34_Inferred1"}, \code{"34_1"}, \code{"34_2"}, etc. #' #' Note that the original sequence identifier is preserved in the \code{orig_sequence_id} field #' and the original parent sequence identifier is retained in \code{orig_parent}. #' #' @seealso See \link{observedMutations} to calculate mutation frequencies using #' \code{parent_sequence} as the reference germline. See \link[alakazam]{ChangeoClone}, #' \link[alakazam]{buildPhylipLineage}, and \link[igraph]{graph} for details on the #' input objects. #' #' @examples #' # Load and subset example data #' data(ExampleDb, package = "alakazam") #' data(ExampleTrees, package = "alakazam") #' graph <- ExampleTrees[[17]] #' db <- subset(ExampleDb, clone_id == graph$clone) #' clone <- alakazam::makeChangeoClone(db) #' #' # Extend data with lineage information #' df <- makeGraphDf(graph, clone) #' #' @export makeGraphDf <- function(curCloneGraph, curCloneObj, objSeqId="sequence_id", objSeq="sequence") { # extracting the cur_clone_num from the inputs to function: cur_clone_num <- curCloneObj@clone # generating a data frame from the clone igraph object # (- for getting the inferred sequences from the graph, # and the parent information): curCloneGraph_df <- summarizeSubtrees(curCloneGraph, fields="sequence") # merging the db from clone object and from graph: cur_clone_merged_df <- merge(x=curCloneObj@data, y=curCloneGraph_df, by.x=objSeqId, by.y="name", all=T) # Renaming sequence_id column to orig_sequence_id, and renaming parent # to orig_parent: cur_clone_merged_df$orig_sequence_id <- cur_clone_merged_df[,objSeqId] cur_clone_merged_df$orig_parent <- cur_clone_merged_df$parent # uniquifying some values in merged data frame, and filling some # missing values: #1. Replacing inferred sequences names with a unique name # (using the clone number). # Doing so for both sequence_id and parent and graph vertices cur_clone_merged_df$parent <- gsub(pattern="Inferred", x=cur_clone_merged_df$parent, replacement=paste("Inferred_", cur_clone_num, "_", sep="")) cur_clone_merged_df[,objSeqId] <- gsub(pattern="Inferred", x=cur_clone_merged_df[,objSeqId], replacement=paste("Inferred_", cur_clone_num, "_", sep="")) V(curCloneGraph)$label <- gsub(pattern="Inferred", x=V(curCloneGraph)$label, replacement=paste("Inferred_", cur_clone_num, "_", sep="")) #2. Replacing Germline sequence name with a unique name # (using the clone number). # Doing so for both sequence_id and parent and graph vertices: cur_clone_merged_df$parent <- gsub(pattern="Germline", x=cur_clone_merged_df$parent, replacement=paste("Germline_", cur_clone_num, sep="")) cur_clone_merged_df$sequence_id <- gsub(pattern="Germline", x=cur_clone_merged_df[,objSeqId], replacement=paste("Germline_", cur_clone_num, sep="")) V(curCloneGraph)$label <- gsub(pattern="Germline", x=V(curCloneGraph)$label, replacement= paste("Germline_", cur_clone_num, sep="")) #3. Now need to fill in missing values for germline sequence and # inffered sequences: cur_clone_merged_df$clone <- cur_clone_num cur_clone_merged_df$v_call <- curCloneObj@v_gene cur_clone_merged_df$j_call <- curCloneObj@j_gene cur_clone_merged_df$junction_length <- curCloneObj@junc_len cur_clone_merged_df$germline_imgt <- curCloneObj@germline #4. setting a new sequence_id column with following format: #_ #Except for Germline and Inferred names which will remain as is. cur_clone_merged_df[,objSeqId] <- paste(cur_clone_num, "_", c(1:length(cur_clone_merged_df$orig_sequence_id)), sep="") cur_clone_merged_df[,objSeqId] <- ifelse(grepl("Germline|Inferred", cur_clone_merged_df$orig_sequence_id), paste(cur_clone_num, "_", cur_clone_merged_df$orig_sequence_id, sep=""), cur_clone_merged_df[,objSeqId]) #5. Doing the same for parent column: # setting a new parent column with following format: # _ #Except for Germline and Inferred names which will remain as is. cur_clone_merged_df$parent <- cur_clone_merged_df[match(cur_clone_merged_df$orig_parent, cur_clone_merged_df$orig_sequence_id), objSeqId] #6. There are 2 sequence columns: one from curCloneGraph_df (names "sequence") # and one from curCloneObj@data (called per argument objSeq). # so first checking if objSeq=="sequence", and taking care accordingly: # Removing the sequence column that came from curCloneObj@data as it does not # include sequences of germline and inferred. # Setting the "sequence" column to be named "sequence" if (objSeq=="sequence") { cur_clone_merged_df <- cur_clone_merged_df %>% select(-!!rlang::sym("sequence.x")) cur_clone_merged_df <- rename(cur_clone_merged_df, sequence=!!rlang::sym("sequence.y")) } else { cur_clone_merged_df1<-cur_clone_merged_df1[!(names(cur_clone_merged_df1) %in% c(objSeq))] } #7. Adding the parent sequence as a new column: cur_clone_merged_df$parent_sequence <- cur_clone_merged_df[match(cur_clone_merged_df$parent, cur_clone_merged_df[,objSeqId]), objSeq] # filling the parent sequece of the Germline sequence to be its own sequence # (=GERMLINE_IMGT): #cur_clone_merged_df <- mutate(cur_clone_merged_df, # parent_sequence=ifelse(is.na(parent_sequence), # GERMLINE_IMGT, # parent_sequence)) # now checking if the germline sequence is equal to its (only) child sequence. # For example if "250_7" sequence parent is the "250_Germline" sequence, # then mergin them to one line called "250_7_Germline". germ_seq_line <- filter(cur_clone_merged_df, !!rlang::sym("orig_sequence_id") == "Germline") germ_seq <- germ_seq_line[,objSeq] germ_son_seq_line <- filter(cur_clone_merged_df, !!rlang::sym("orig_parent") == "Germline") germ_son_seq <- germ_son_seq_line[,objSeq] if (seqDist(germ_seq, germ_son_seq) == 0) { # removing from db the line of the germline: cur_clone_merged_df <- filter(cur_clone_merged_df, !!rlang::sym("orig_sequence_id") != "Germline") # renaming the sequence id of the germline son - to include "Germline" # in its name: cur_clone_merged_df[,objSeqId]<-ifelse(cur_clone_merged_df[,"orig_parent"] == "Germline", paste(cur_clone_merged_df[,objSeqId], "_", "Germline", sep=""), cur_clone_merged_df[, objSeqId]) # Change the parent SEQUENCE to be NA (as it is the Germline) cur_clone_merged_df <- mutate(cur_clone_merged_df, parent=ifelse(!!rlang::sym("orig_parent") == "Germline", "NA", !!rlang::sym("parent"))) } return(cur_clone_merged_df) } #' Build a RegionDefinition object that includes CDR3 and FWR4. #' #' \code{setRegionBoundaries} takes as input a junction length and an IMGT-numbered sequence #' and outputs a custom \code{RegionDefinition} object that includes the boundary definitions of #' CDR1-3 and FWR1-4 for that sequence. In contrast to the universal \code{RegionDefinition} object #' that end with FWR3, the returned definition is per-sequence due to variable junction lengths. #' #' @param juncLength junction length of the sequence. #' @param sequenceImgt IMGT-numbered sequence. #' @param regionDefinition \code{RegionDefinition} type to calculate the region definition for. #' Can be one of \code{IMGT_VDJ_BY_REGIONS} or \code{IMGT_VDJ}, #' which are template definitions that include CDR1-3 and FWR1-4. #' Only these two regions include all CDR1-3 and FWR1-4 regions. #' If this argument is set to \code{NULL}, then an empty #' \code{RegionDefinition} will be returned. #' #' @return A \code{RegionDefinition} object that includes CDR1-3 and FWR1-4 for the #' \code{sequenceImgt}, \code{juncLength}, and \code{regionDefinition} specified. #' #' For \code{regionDefinition=IMGT_VDJ_BY_REGIONS}, the returned \code{RegionDefinition} #' includes: #' #' \itemize{ #' \item \code{fwr1}: Positions 1 to 78. #' \item \code{cdr1}: Positions 79 to 114. #' \item \code{fwr2}: Positions 115 to 165. #' \item \code{cdr2}: Positions 166 to 195. #' \item \code{fwr3}: Positions 196 to 312. #' \item \code{cdr3}: Positions 313 to (313 + juncLength - 6) since the junction #' sequence includes (on the left) the last codon from FWR3 and #' (on the right) the first codon from FWR4. #' \item \code{fwr4}: Positions (313 + juncLength - 6 + 1) to the end of the sequence. #' } #' #' For \code{regionDefinition=IMGT_VDJ}, the returned \code{RegionDefinition} includes: #' #' \itemize{ #' \item \code{fwr}: Positions belonging to a FWR. #' \item \code{cdr}: Positions belonging to a CDR. #' } #' #' In the case that the \code{regionDefinition} argument is not one of the extended #' regions (\code{IMGT_VDJ_BY_REGIONS} or \code{IMGT_VDJ}), the input #' \code{regionDefinition} is returned as is. #' #' @seealso See \link{RegionDefinition} for the return object. #' See \link{IMGT_SCHEMES} for a set of predefined \code{RegionDefinition} objects. #' #' @examples #' # Load and subset example data #' data(ExampleDb, package = "alakazam") #' len <- ExampleDb$junction_length[1] #' sequence <- ExampleDb$sequence_alignment[1] #' region <- setRegionBoundaries(len, sequence, regionDefinition = IMGT_VDJ) #' #' @export setRegionBoundaries <- function(juncLength, sequenceImgt, regionDefinition=NULL) { # Check RegionDefinition input if (is.null(regionDefinition)) { rd <- makeNullRegionDefinition(nchar(sequenceImgt)) return(rd) } else if (!is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } else if (!(regionDefinition@name %in% c("IMGT_VDJ_BY_REGIONS", "IMGT_VDJ"))) { return(regionDefinition) } # all slots except for boundaries and seqLength are already defined in regionDefinition # First need to extract sequence length from sequence: seqLength <- nchar(sequenceImgt) # juncLength doesn't include alignment gaps, which are in sequenceImgt # and need to be added to correctly identify the boundaries # Also, sequence_alignment can have `.` that represent indels junction_length_helper <- !strsplit(sequenceImgt[[1]], "")[[1]] %in% c("-", ".") junction_length_helper[1:310 - 1] <- 0 if (juncLength > 0) { junction_end <- which(cumsum(junction_length_helper[1:length(junction_length_helper)])==juncLength[[1]])[1] if (is.na(junction_end)) { warning("junction ends past 'sequenceImgt'") junction_end <- nchar(sequenceImgt) num_gaps <- sum(!junction_length_helper[310:junction_end]) cdr3_end <- junction_end } else { num_gaps <- sum(!junction_length_helper[310:junction_end]) juncLength <- juncLength + num_gaps cdr3_end <- 313 + as.integer(juncLength) - 6 - 1 } } else { cdr3_end <- 0 } # now for the boundaries slot: boundaries <- factor(shazam::IMGT_V_BY_REGIONS@boundaries, levels=c(levels(shazam::IMGT_V_BY_REGIONS@boundaries), "cdr3", "fwr4")) if (cdr3_end > 312) { boundaries[313:cdr3_end] <- factor("cdr3") if (cdr3_end < nchar(sequenceImgt)) { boundaries[(cdr3_end+1):seqLength] <- factor("fwr4") } } else { # If you are here, the junction is too short, <= 6nt warning("CDR3 end < CDR3 start. Couldn't identify CDR3 and FWR4. Aligned junction length is: ", juncLength) } # build RegionDefinition object if (regionDefinition@name == "IMGT_VDJ") { boundaries <- gsub(pattern="fwr.", replacement = "fwr", x=boundaries, perl=TRUE) boundaries <- gsub(pattern="cdr.", replacement = "cdr", x=boundaries, perl=TRUE) boundaries <- factor(boundaries, levels=c("fwr", "cdr")) } rd <- new("RegionDefinition", name=regionDefinition@name, description=regionDefinition@description, boundaries=boundaries, seqLength=unname(seqLength), regions=regionDefinition@regions, labels=regionDefinition@labels, citation=regionDefinition@citation) return(rd) } # Calculating an extended (=that includes cdr1/2/3 and fwr1/2/3/4) region definition # for a specific clone in database. # Inputs: # - clone_num: the clone number for which to calculate the region definition. # - db: a ChangeoClone database that includes clone numbers. # - seq_col: the name of the db column containing the sequence that is imgt aligned. # - juncLengthColumn: the name of the db column containing the junction length. # - clone_col: the name of the db column containing the clone number. # - regionDefinition: the region definition type to be output for this clone. # Output: # A regionDefinition object for the specific clone # Note: regionDefinition needs to be calculated specifically for the clone if it # is of type IMGT_VDJ or IMGT_VDJ_BY_REGIONS, as it includes also cdr3 and fwr4 # which are specific to clone. # Note: The region definition is same for all sequences in clone - so doing it # based on first sequence in clone. getCloneRegion <- function(clone_num, db, seq_col="sequence", juncLengthColumn="junction_length", clone_col="clone", regionDefinition=NULL) { # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # subseting the db to lines for specific clone clone_db <- db[db[[clone_col]] == clone_num,] if ( length(unique(clone_db[[juncLengthColumn]])) >1 ) { stop("Expecting clones where all sequences have the same junction lenth. Different lengths found for clone ", clone_num) } # getting one of the sequences of the specific clone: seq <- clone_db[[seq_col]][1] junc_len <- clone_db[[juncLengthColumn]][1] reg <- setRegionBoundaries(juncLength=junc_len, sequenceImgt=seq, regionDefinition=regionDefinition) return(reg) } # Status: experimental, not exported function # data(SingleDb, package="alakazam") # germline_db <- list( # "IGHV3-11*05"="CAGGTGCAGCTGGTGGAGTCTGGGGGA...GGCTTGGTCAAGCCTGGAGGGTCCCTGAGACTCTCCTGTGCAGCCTCTGGATTCACCTTC............AGTGACTACTACATGAGCTGGATCCGCCAGGCTCCAGGGAAGGGGCTGGAGTGGGTTTCATACATTAGTAGTAGT......AGTAGTTACACAAACTACGCAGACTCTGTGAAG...GGCCGATTCACCATCTCCAGAGACAACGCCAAGAACTCACTGTATCTGCAAATGAACAGCCTGAGAGCCGAGGACACGGCCGTGTATTACTGTGCGAGAGA", # "IGHD3-10*01"="GTATTACTATGGTTCGGGGAGTTATTATAAC", # "IGHJ5*02"="ACAACTGGTTCGACCCCTGGGGCCAGGGAACCCTGGTCACCGTCTCCTCAG" # ) # pja <- shazam:::plotJunctionAlignment(SingleDb, germline_db,regionDefinition=IMGT_VDJ_BY_REGIONS) # pja$p plotJunctionAlignment <- function(db_row, germline_db, sequence_alignment="sequence_alignment", v_call="v_call", d_call="d_call", j_call="j_call", v_germline_start="v_germline_start", v_germline_end="v_germline_end", d_germline_start="d_germline_start", d_germline_end="d_germline_end", j_germline_start="j_germline_start", j_germline_end="j_germline_end", np1_length="np1_length", # np2_length="np2_length", junction="junction", junction_length="junction_length", germline_alignment="germline_alignment", regionDefinition=NULL) { # Check for valid columns check <- checkColumns(db_row, c(sequence_alignment, v_call, j_call, # d_call, v_germline_start, v_germline_end, # d_germline_start, # d_germline_end, j_germline_start, j_germline_end, np1_length, # np2_length, junction, junction_length) ) if (check != TRUE) { stop(check) } if (!is.null(d_call)) { d_columns <- c(d_call, d_germline_start, d_germline_end) found <- d_columns %in% colnames(db_row) if (any(!found)) { stop( "Column(s) ", paste(d_columns[!found], sep="", collpase=",") ," not found.") } } if (!germline_alignment %in% colnames(db_row)) { warning("The column germline_alignment doesn't exist. Assigned NA value.") db_row[[germline_alignment]] <- NA } # Check region definition if (!is.null(regionDefinition)) { if (!is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } } junction_seq <- db_row[[junction]] v_allele <- getAllele(db_row[[v_call]], first=T) d_allele <- getAllele(db_row[[d_call]], first=T) j_allele <- getAllele(db_row[[j_call]], first=T) #if (!is.na(d_allele)) { v_germline <- germline_db[[v_allele]] if (!is.na(d_allele)) { d_germline <- germline_db[[d_allele]] } j_germline <- germline_db[[j_allele]] seq_aln <- db_row[[sequence_alignment]] germ_aln <- db_row[[germline_alignment]] getPositionDf <- function(seq, label) { sequence_chars <- strsplit(seq,"")[[1]] sequence_chars position_df <- data.frame("nucleotide"=sequence_chars, "pos"=1:nchar(seq), "label"=label, stringsAsFactors = F) position_df } sequence_aln_df <- getPositionDf(seq_aln,sequence_alignment) sequence_aln_df[['aligned']] <- T if (!is.na(germ_aln)) { germ_aln_df <- getPositionDf(germ_aln,germline_alignment) germ_aln_df[['aligned']] <- T } else { germ_aln_df <- data.frame() } v_germline_df <- getPositionDf(v_germline,"v_germline") v_germ_start <- as.numeric(db_row[[v_germline_start]]) v_germ_end <- as.numeric(db_row[[v_germline_end]]) v_germline_df[['aligned']] <- v_germline_df$pos >= v_germ_start & v_germline_df$pos <= v_germ_end v_germ_start_off <- 1 - v_germ_start v_germline_df$pos <- v_germline_df$pos + v_germ_start_off v_length <- v_germ_end - v_germ_start + 1 np1_len <- db_row[[np1_length]] if (!is.na(d_allele)) { d_germline_df <- getPositionDf(d_germline,"d_germline") d_germ_start <- as.numeric(db_row[[d_germline_start]]) d_germ_end <- as.numeric(db_row[[d_germline_end]]) d_germline_df[['aligned']] <- d_germline_df$pos >= d_germ_start & d_germline_df$pos <= d_germ_end d_germ_start_off <- 1-d_germ_start d_germline_df$pos <- v_length + np1_len + d_germline_df$pos + d_germ_start_off # d_length <- d_germ_end - d_germ_start + 1 } else { d_germline_df <- data.frame() } # np2_len <- db_row[[np2_length]] j_germline_df <- getPositionDf(j_germline,"j_germline") j_germ_start <- as.numeric(db_row[[j_germline_start]]) j_germ_end <- as.numeric(db_row[[j_germline_end]]) j_germline_df[['aligned']] <- j_germline_df$pos >= j_germ_start & j_germline_df$pos <= j_germ_end j_length <- j_germ_end - j_germ_start + 1 # Use end as reference j_germline_df$pos <- nchar(seq_aln) - j_length + 1 + j_germline_df$pos - j_germ_start df <- bind_rows(sequence_aln_df, v_germline_df, d_germline_df, j_germline_df, germ_aln_df) if (is.na(junction_seq) | db_row[[junction_length]]<1 ) { message("No junction available for this sequence.") junction_df <- NULL } else { # junction_position <- #stri_locate(seq_aln,fixed=junction_seq) junction_start <- 310 j_len <- db_row[[junction_length]] # junction_end <- junction_start + j_len - 1 # Get aligned junction, with gaps junction_alignment_helper <- !stringi::stri_split_boundaries(seq_aln, type="char")[[1]] %in% c("-",".") junction_alignment_helper[1:junction_start-1] <- 0 junction_end <- which(cumsum(junction_alignment_helper[1:length(junction_alignment_helper)])>j_len)[1] - 1 if (is.na(junction_end)) { warning("The junction ends past sequence_alignment. Using the last position as junction_end.") junction_end <- length(junction_alignment_helper) } junction_alignment <- stringi::stri_sub(seq_aln,310,junction_end) # junction_end <- junction_start + nchar(db_row[[junction]]) -1 junction_df <- data.frame("nucleotide"=strsplit(junction_alignment,"")[[1]], stringsAsFactors = F) junction_df[['pos']] <- c(junction_start:junction_end) junction_df[['label']] <- "junction" junction_df[['aligned']] <- T } missing_junction_nt <- db_row[[junction_length]] - sum(junction_df$aligned) if ( missing_junction_nt > 0 ) { junction_nt <- gsub("[\\.\\-]","",db_row[['junction']]) missing_nt <- stringi::stri_sub(junction_nt, nchar(junction_nt)-missing_junction_nt+1, nchar(junction_nt)) junction_df_missing <- getPositionDf(missing_nt,"junction") junction_df_missing[['aligned']] <- F junction_df_missing[['pos']] <- junction_df_missing[['pos']] + max(junction_df[['pos']]) junction_df <- bind_rows(junction_df, junction_df_missing) } # addRegionDefinition boundaries region_definition <- setRegionBoundaries(db_row[[junction_length]], db_row[[sequence_alignment]], regionDefinition ) if (!is.null(regionDefinition)) { rdf <- data.frame( "nucleotide"=as.character(region_definition@boundaries), "pos"=1:length(region_definition@boundaries), "label"="region_definition", "aligned"=T, stringsAsFactors = F) } else { rdf <- data.frame() } df <- bind_rows(df, junction_df, rdf) ordered_labels <- c("region_definition",sequence_alignment, "junction", "v_germline", "d_germline","j_germline", germline_alignment) df[['label']] <- factor(df[['label']], levels=rev(ordered_labels), ordered = T) color_palette <- c( "A" = "lightskyblue2", "T" = "khaki2", "G" = "palegreen3", "C" = "coral2", "." = "white", "N" = "grey80", "-" = "black", "cdr1" = "#FFDCD2", "cdr2" = "#FFDCD2", "cdr3" = "#FFB189", "fwr1" = "#8CCEDB", "fwr2" = "#8CCEDB", "fwr3" = "#8CCEDB", "fwr4" = "#8CCEDB" ) fig_theme <- function(font_size=7) { theme_bw() + theme(text=element_text(size=font_size), axis.title=element_text(size=font_size), axis.text=element_text(size=font_size), axis.text.x=element_text(size=font_size), axis.text.y=element_text(size=font_size), #axis.ticks=element_blank(), #axis.ticks=theme_segment(colour = "black"), #panel.background=element_rect(fill = NA, colour = "black", linewidth = 0.25), panel.border=element_blank(), #panel.grid.major=element_line(colour = "grey", size = 0.05), #panel.grid.minor=element_line(colour = "grey", size = 0.05), panel.grid.major.y=element_blank(), panel.grid.minor.y=element_blank(), panel.grid.major.x=element_blank(), panel.grid.minor.x=element_blank(), panel.spacing=unit(0.25, "lines"), plot.title=element_text(size=font_size, face="bold", lineheight=0.8), legend.position="top", legend.text=element_text(size=font_size), # legend.title = element_text(size=font_size), legend.title=element_blank(), legend.spacing=unit(0.25, "lines"), legend.box="horizontal", legend.box.spacing=unit(0.25, "lines"), legend.key.height=unit(1,"line"), legend.key.width=unit(1,"line"), strip.text = element_text(size = font_size, face="plain"), strip.background = element_blank(), plot.margin= unit(c(0, 0, 0, 0), "lines")) } p <- ggplot(data=df %>% dplyr::filter(.data$label == "region_definition") %>% dplyr::mutate(label=factor(.data$label, levels = ordered_labels, ordered = T)), aes(x=.data$pos, y=.data$label, fill=.data$nucleotide, alpha=.data$aligned)) + geom_tile( height=0.3) + geom_tile(data=df %>% dplyr::filter(.data$label != "region_definition") %>% dplyr::mutate(label=factor(.data$label, levels = ordered_labels, ordered = T)), color="grey50") + fig_theme() + scale_fill_manual(values=color_palette) + scale_alpha_manual(values=c("TRUE"=1, "FALSE"=0.2), guide="none") + scale_y_discrete(breaks=ordered_labels, labels=ordered_labels, limits=rev(ordered_labels), expand=c(0, 0)) + ylab("") + xlab("IMGT position") + guides(fill=guide_legend(nrow=1)) + scale_x_continuous(expand=c(0, 0)) list(p=p, data=df) } shazam/R/Deprecated.R0000644000176200001440000001753714314355031014135 0ustar liggesusers# Deprecated and defunct functions #' @include MutationProfiling.R NULL #### Deprecated #### #' slideWindowTunePlot - plotSlideWindowTune backward compatability #' #' Wrapper function for \link{plotSlideWindowTune} #' #' @param tuneList a list of logical matrices returned by \link{slideWindowTune}. #' @param plotFiltered whether to plot the number of filtered (\code{TRUE} or \code{filtered}), #' or remaining (FALSE or remaining) sequences for each mutation threshold. #' Use \code{NULL} or \code{per_mutation} to plot the number of sequences #' at each mutation value. Default is \code{TRUE}. #' @param percentage whether to plot on the y-axis the percentage of filtered sequences #' (as opposed to the absolute number). Default is \code{FALSE}. #' @param jitter.x whether to jitter x-axis values. Default is \code{FALSE}. #' @param jitter.x.amt amount of jittering to be applied on x-axis values if #' \code{jitter.x=TRUE}. Default is 0.1. #' @param jitter.y whether to jitter y-axis values. Default is \code{FALSE}. #' @param jitter.y.amt amount of jittering to be applied on y-axis values if #' \code{jitter.y=TRUE}. Default is 0.1. #' @param pchs point types to pass on to \link{plot}. #' @param ltys line types to pass on to \link{plot}. #' @param cols colors to pass on to \link{plot}. #' @param plotLegend whether to plot legend. Default is \code{TRUE}. #' @param legendPos position of legend to pass on to \link{legend}. Can be either a #' numeric vector specifying x-y coordinates, or one of #' \code{"topright"}, \code{"center"}, etc. Default is \code{"topright"}. #' @param legendHoriz whether to make legend horizontal. Default is \code{FALSE}. #' @param legendCex numeric values by which legend should be magnified relative to 1. #' @param title plot main title. Default is NULL (no title) #' @param returnRaw Return a data.frame with sequence counts (TRUE) or a #' plot. Default is \code{FALSE}. #' #' @details For each \code{windowSize}, if \code{plotFiltered=TRUE}, the x-axis #' represents a mutation threshold range, and the y-axis the number of #' sequences that have at least that number of mutations. If #' \code{plotFiltered=TRUE}, the y-axis represents the number of sequences #' that have less mutations than the mutation threshold range. For the same #' window size, a sequence can be included in the counts for different #' mutation thresholds. For example, sequence "CCACCAAAA" with germline #' "AAAAAAAAA" has 4 mutations. This sequence has at least 2 mutations #' and at least 3 mutations, in a window of size 4. the sequence will #' be included in the sequence count for mutation thresholds 2 and 3. #' If \code{plotFiltered=TRUE}, the sequences are counted only once for #' each window size, at their largest mutation threshold. The above #' example sequence would be included in the sequence count for #' mutation threshold 3. #' #' When plotting, a user-defined \code{amount} of jittering can be applied on values plotted #' on either axis or both axes via adjusting \code{jitter.x}, \code{jitter.y}, #' \code{jitter.x.amt} and \code{jitter.y.amt}. This may be help with visually distinguishing #' lines for different window sizes in case they are very close or identical to each other. #' If plotting percentages (\code{percentage=TRUE}) and using jittering on the y-axis values #' (\code{jitter.y=TRUE}), it is strongly recommended that \code{jitter.y.amt} be set very #' small (e.g. 0.01). #' #' \code{NA} for a combination of \code{mutThresh} and \code{windowSize} where #' \code{mutThresh} is greater than \code{windowSize} will not be plotted. #' #' @seealso See \link{slideWindowTune} for how to get \code{tuneList}. See \link{jitter} for #' use of \code{amount} of jittering. #' #' @examples #' # Use an entry in the example data for input and germline sequence #' data(ExampleDb, package="alakazam") #' #' # Try out thresholds of 2-4 mutations in window sizes of 3-5 nucleotides #' # on a subset of ExampleDb #' tuneList <- slideWindowTune(db = ExampleDb[1:10, ], #' mutThreshRange = 2:4, windowSizeRange = 3:5, #' verbose = FALSE) #' #' # Visualize #' # Plot numbers of sequences filtered without jittering y-axis values #' slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered=TRUE, jitter.y=FALSE) #' #' # Notice that some of the lines overlap #' # Jittering could help #' slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered=TRUE, jitter.y=TRUE) #' #' # Plot numbers of sequences remaining instead of filtered #' slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered=FALSE, jitter.y=TRUE, #' legendPos="bottomright") #' #' # Plot percentages of sequences filtered with a tiny amount of jittering #' slideWindowTunePlot(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered=TRUE, percentage=TRUE, #' jitter.y=TRUE, jitter.y.amt=0.01) #' @export slideWindowTunePlot <- function(tuneList, plotFiltered = c(TRUE,FALSE,NULL,'filtered','remaining','per_mutation'), percentage = FALSE, jitter.x = FALSE, jitter.x.amt = 0.1, jitter.y = FALSE, jitter.y.amt = 0.1, pchs = 1, ltys = 2, cols = 1, plotLegend = TRUE, legendPos = "topright", legendHoriz = FALSE, legendCex = 1, title=NULL, returnRaw=FALSE){ .Deprecated("plotSlideWindowTune", msg="slideWindowTunePlot() is deprecated, please see plotSlideWindowTune() for future use") # input validation plotFiltered_choices <- c(TRUE,FALSE,NULL,'filtered','remaining','per_mutation') plotFiltered <- plotFiltered[1] if (!is.null(plotFiltered)) { if (!plotFiltered %in% plotFiltered_choices) { stop("`plotFiltered` must be one of: ", paste(plotFiltered_choices,collapse=", ")) } } # logic for converting T/F/NULL to new values if (is.null(plotFiltered)) { plotFilteredMapped <- 'per_mutation' } else if (plotFiltered %in% c(TRUE,'filtered')) { plotFilteredMapped <- 'filtered' } else if (plotFiltered %in% c(FALSE,'remaining')) { plotFilteredMapped <- 'remaining' } else { plotFilteredMapped <- 'per_mutation' } plotSlideWindowTune(tuneList, plotFiltered = plotFilteredMapped, percentage = percentage, jitter.x = jitter.x, jitter.x.amt = jitter.x.amt, jitter.y = jitter.y, jitter.y.amt = jitter.y.amt, pchs = pchs, ltys = ltys, cols = cols, plotLegend = plotLegend, legendPos = legendPos, legendHoriz = legendHoriz, legendCex = legendCex, title=title, returnRaw=returnRaw) }shazam/R/RegionDefinitions.R0000644000176200001440000002175714067124024015514 0ustar liggesusers# Class definitions for sequence regions #' @include Shazam.R #' @include Core.R NULL #### Constants #### # Region color palette REGION_PALETTE <- c("cdr"="#377eb8", "fwr"="#e41a1c", "cdr1"="#ff7f00", "cdr2"="#a65628", "cdr3"="#a62828", "fwr1"="#4daf4a", "fwr2"="#984ea3", "fwr3"="#e41a1c", "fwr4"="#908cff") #### Classes #### #' S4 class defining a region definition #' #' \code{RegionDefinition} defines a common data structure for defining the region #' boundaries of an Ig sequence. #' #' @slot name name of the RegionDefinition. #' @slot description description of the model and its source. #' @slot boundaries \code{factor} defining the region boundaries of the #' sequence. The levels and values of \code{boundaries} #' determine the number of regions. #' @slot seqLength length of the sequence. #' @slot regions levels of the boundaries; e.g, \code{c("cdr", "fwr")}. #' @slot labels labels for the boundary and mutations combinations; #' e.g., \code{c("cdr_r", "cdr_s", "fwr_r", "fwr_s")}. #' @slot citation publication source. #' #' @seealso #' See \link{IMGT_SCHEMES} for a set of predefined \code{RegionDefinition} objects. #' #' @name RegionDefinition-class #' @rdname RegionDefinition-class #' @aliases RegionDefinition #' @exportClass RegionDefinition setClass("RegionDefinition", slots=c(name="character", description="character", boundaries="factor", seqLength="numeric", regions="character", labels="character", citation="character"), prototype=list(name="IMGT_V", description="IMGT_Numbering scheme defining the V gene up to, but not including, CDR3.", boundaries=factor(c(rep("fwr", 78), rep("cdr", 36), rep("fwr", 51), rep("cdr", 30), rep("fwr", 117)), levels=c("cdr","fwr")), seqLength=312, regions=c("cdr", "fwr"), labels=c("cdr_r", "cdr_s", "fwr_r", "fwr_s"), citation="Lefranc MP et al. (2003)")) #### RegionDefinition building functions ##### #' Creates a RegionDefinition #' #' \code{createRegionDefinition} creates a \code{RegionDefinition}. #' #' @param name name of the region definition. #' @param boundaries \code{factor} defining the region boundaries of the sequence. #' The levels and values of \code{boundaries} determine the #' number of regions (e.g. CDR and FWR). #' @param description description of the region definition and its source data. #' @param citation publication source. #' #' @return A \code{RegionDefinition} object. #' #' @seealso See \link{RegionDefinition} for the return object. #' #' @examples #' # Creates an empty RegionDefinition object #' createRegionDefinition() #' #' @export createRegionDefinition <- function(name="", boundaries=factor(), description="", citation="") { #Extract information from 'boundaries' # Determine the number of levels (e.g. cdr, fwr) regions <- levels(boundaries) # Determine the length of the boundaries seqLength <- length(boundaries) # Determine the combinations of levels_regionDefinition and R/S # e.g. cdr_r, cdr_s, fwr_r, fwr_s labels <- paste(rep(regions, each=2), rep(c("r", "s"), length(regions)), sep="_") # Define RegionDefinition object regionDefinition <- new("RegionDefinition", name=name, description=description, boundaries=boundaries, seqLength=seqLength, regions=regions, labels=labels, citation=citation) return(regionDefinition) } # Create an empty RegionDefinition object # # \code{makeNullRegionDefinition} takes an array of observed mutations # and makes an empty RegionDefinition object. # # @param regionLength Length of the empty # # @return A \code{RegionDefinition} object makeNullRegionDefinition <- function(regionLength) { rd <- createRegionDefinition(name="", boundaries=factor(c(rep("seq", regionLength)), levels = c("seq")), description="", citation="") return(rd) } #### Data #### #' IMGT unique numbering schemes #' #' Sequence region definitions according to the IMGT unique numbering scheme. #' #' @format A \link{RegionDefinition} object defining: #' \itemize{ #' \item \code{IMGT_V}: The IMGT numbered V segment up to position nucleotide 312. #' This definition combines the CDR1 and CDR2 into a single CDR region, #' and FWR1, FWR2 and FWR3 into a single FWR region. CDR3 and FWR4 are #' excluded as they are downstream of nucleotide 312. #' \item \code{IMGT_V_BY_CODONS}: The IMGT numbered V segment up to position nucleotide 312. #' This definition treats each codon, from codon 1 to codon 104, as a #' distinct region. #' \item \code{IMGT_V_BY_REGIONS}: The IMGT numbered V segment up to position nucleotide 312. #' This defines separate regions for each of CDR1, CDR2, #' FWR1, FWR2 and FWR3. CDR3 and FWR4 are #' excluded as they are downstream of nucleotide 312. #' \item \code{IMGT_V_BY_SEGMENTS}: The IMGT numbered V segment up to position nucleotide 312. #' This definition has no subdivisons and treats the entire V segment #' as a single region. #' \item \code{IMGT_VDJ}: IMGT numbered regions for CDR1-3 and FWR1-4 with combined CDR and FWR #' definitions spanning CDR1-3 and FWR1-4, respectively. #' Note, unless the definition object has been updated using \link{setRegionBoundaries} #' this schema will have a value of \code{0} for the \code{seqLength} slot and #' the \code{boundaries} slot will be empty. This is because #' these slots depend on the junction length which is unknown in the template #' scheme. After \link{setRegionBoundaries} has been run, these slots will be populated #' with the appropriate values for the specied sequence and junction length. #' \item \code{IMGT_VDJ_BY_REGIONS}: The IMGT numbered regions for FWR1-4 and CDR1-3 with separate region boundaries #' for each of CDR1, CDR2, CDR3, FWR1, FWR2, FWR3 and FWR4. #' Note, unless the definition object has been updated using \link{setRegionBoundaries} #' this schema will have a value of \code{0} for the \code{seqLength} slot and #' the \code{boundaries} slot will be empty. This is because #' these slots depend on the junction length which is unknown in the template #' scheme. After \link{setRegionBoundaries} has been run, these slots will be populated #' with the appropriate values for the specied sequence and junction length. #' } #' #' @references #' \enumerate{ #' \item Lefranc MP, et al. IMGT unique numbering for immunoglobulin and T cell #' receptor variable domains and Ig superfamily V-like domains. #' Developmental and comparative immunology. 2003 27:55-77. #' } #' #' @name IMGT_SCHEMES NULL #' @name IMGT_V #' @rdname IMGT_SCHEMES NULL #' @name IMGT_V_BY_CODONS #' @rdname IMGT_SCHEMES NULL #' @name IMGT_V_BY_REGIONS #' @rdname IMGT_SCHEMES NULL #' @name IMGT_V_BY_SEGMENTS #' @rdname IMGT_SCHEMES NULL #' @name IMGT_VDJ_BY_REGIONS #' @rdname IMGT_SCHEMES NULL #' @name IMGT_VDJ #' @rdname IMGT_SCHEMES NULL shazam/R/Shmulate.R0000644000176200001440000004121413715006542013650 0ustar liggesusers# SHMulate #' @include Shazam.R #' @include Core.R #' @include MutationProfiling.R NULL #### SHMulation #### #' Simulate mutations in a single sequence #' #' Generates random mutations in a sequence iteratively using a targeting model. #' Targeting probabilities at each position are updated after each iteration. #' #' @param sequence sequence string in which mutations are to be introduced. #' Accepted alphabet: \code{\{A, T, G, C, N, .\}}. Note #' that \code{-} is not accepted. #' @param numMutations a whole number indicating the number of mutations to be #' introduced into \code{sequence}, if \code{frequency=FALSE}. #' A fraction bewteen 0 and 1 indicating the mutation frequency #' if \code{frequency=TRUE}. #' @param targetingModel 5-mer \link{TargetingModel} object to be used for computing #' probabilities of mutations at each position. Defaults to #' \link{HH_S5F}. #' @param start Initial position in \code{sequence} where mutations can #' be introduced. Default: 1 #' @param end Last position in \code{sequence} where mutations can #' be introduced. Default: last position (sequence length). #' @param frequency If \code{TRUE}, treat \code{numMutations} as a frequency. #' #' @return A string defining the mutated sequence. #' #' @details #' If the input \code{sequence} has a non-triplet overhang at the end, it will be trimmed #' to the last codon. For example, \code{ATGCATGC} will be trimmed to \code{ATGCAT}. #' #' Mutations are not introduced to positions in the input \code{sequence} that contain #' \code{.} or \code{N}. #' #' With \code{frequency=TRUE}, the number of mutations introduced is the \code{floor} of #' the length of the sequence multiplied by the mutation frequency specified via #' \code{numMutations}. #' #' @seealso See \link{shmulateTree} for imposing mutations on a lineage tree. #' See \link{HH_S5F} and \link{MK_RS5NF} for predefined #' \link{TargetingModel} objects. #' #' @examples #' # Define example input sequence #' sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATA.TTTA" #' #' # Simulate using the default human 5-mer targeting model #' # Introduce 6 mutations #' shmulateSeq(sequence, numMutations=6, frequency=FALSE) #' #' # Introduction 5% mutations #' shmulateSeq(sequence, numMutations=0.05, frequency=TRUE) #' #' @export shmulateSeq <- function(sequence, numMutations, targetingModel=HH_S5F, start=1, end=nchar(sequence), frequency=FALSE) { #* counts on constant variables CODON_TABLE, NUCLEOTIDES (ACTGN-.) if (!frequency) { # check if numMutations is a whole number # is.wholenumber function borrowed from R's integer help is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) abs(x - round(x)) < tol if (!is.wholenumber(numMutations)) { stop("`numMutations` must be a whole number for frequency=FALSE.") } } else { # check if numMutations if between 0 and 1 if (!(numMutations>=0 & numMutations<=1)) { stop("`numMutations` must be a fraction between 0 and 1 for frequency=TRUE.") } } # Check targeting model if (!is(targetingModel, "TargetingModel")) { stop(deparse(substitute(targetingModel)), " is not a valid TargetingModel object") } # Trim sequence to consider only the interval start:end head_sequence <- "" tail_sequence <- "" seq_len <- stri_length(sequence) if (start<1 | end>seq_len ) { stop("`start` must be >= 1 and `end` must be <= sequence length") } else { head_sequence <- stri_sub(str=sequence, from=0, to=start-1) tail_sequence <- stri_sub(str=sequence, from=end+1, to=seq_len) sequence <- stri_sub(str=sequence, from=start, to=end) } # Trim sequence to last codon (getCodonPos from MutationProfiling.R) if(getCodonPos(stri_length(sequence))[3] > stri_length(sequence)) { warning("Trimming sequence to last codon") sim_seq <- stri_sub(str=sequence, from=1, to=getCodonPos(stri_length(sequence))[1]-1) # Add removed chars to tail_sequence tail_sequence <- paste0( stri_sub(str=sequence, from=getCodonPos(stri_length(sequence))[1], to=stri_length(sequence)), tail_sequence) } else { sim_seq <- sequence } sim_leng <- stri_length(sim_seq) stopifnot((sim_leng %% 3)==0) # if specifying mutation frequency instead of count, # get corresponding mutation count based on sequence length if (frequency) { numMutations <- floor(sim_leng*numMutations) } if (numMutations > sim_leng) { stop("Number of mutations specified is larger than the length of the sequence.") } # Calculate possible mutations (given codon table) mutation_types <- computeMutationTypes(sim_seq) # Calculate probabilities of mutations at each position given targeting # from MutationProfiling.R; includes a N row # Columns corresponding to "N" and "." positions will have NA across all rows # These get converted to a probability of 0, ensuring that sampleMut() will # never choose these positions targeting <- calculateTargeting(germlineSeq = sim_seq, targetingModel = targetingModel) # keep only ACGT rows targeting <- targeting[NUCLEOTIDES[1:4], ] # set NA to 0 targeting[is.na(targeting)] <- 0 # Make probability of stop codon 0 targeting[mutation_types=="stop"] <- 0 # Initialize counters total_muts <- 0 positions <- numeric(numMutations) while (total_muts < numMutations) { # Get position to mutate and update counters mutpos <- sampleMut(sim_leng, targeting, positions) total_muts <- total_muts + 1 positions[total_muts] <- mutpos$pos # Implement mutation in simulation sequence mut_nuc <- 4 - (4*mutpos$pos - mutpos$mut) # stri_sub(str=sim_seq, from=mutpos$pos, to=mutpos$pos) <- NUCLEOTIDES[mut_nuc] sim_seq <- stri_sub_replace(str=sim_seq, from=mutpos$pos, to=mutpos$pos, value=NUCLEOTIDES[mut_nuc]) # Update targeting lower <- max(mutpos$pos-4, 1) upper <- min(mutpos$pos+4, sim_leng) targeting[, lower:upper] <- calculateTargeting(germlineSeq = stri_sub(str = sim_seq, from = lower, to = upper), targetingModel = targetingModel)[NUCLEOTIDES[1:4], ] targeting[is.na(targeting)] <- 0 # Update possible mutations lower <- getCodonPos(lower)[1] upper <- getCodonPos(upper)[3] mutation_types[, lower:upper] <- computeMutationTypes(stri_sub(str = sim_seq, from = lower, to = upper)) # Make probability of stop codon 0 if (any(mutation_types[, lower:upper]=="stop", na.rm=T)) { targeting[, lower:upper][mutation_types[, lower:upper]=="stop"] <- 0 } } # sanity check: length of sim_seq should remain unchanged after simulation stopifnot(sim_leng==stri_length(sim_seq)) # Add back head and tail sequences sim_seq <- paste0(head_sequence, sim_seq, tail_sequence) return(sim_seq) } #' Simulate mutations in a lineage tree #' #' \code{shmulateTree} returns a set of simulated sequences generated from an input #' sequence and a lineage tree. The input sequence is used to replace the most recent #' common ancestor (MRCA) node of the \code{igraph} object defining the lineage tree. #' Sequences are then simulated with mutations corresponding to edge weights in the tree. #' Sequences will not be generated for groups of nodes that are specified to be excluded. #' #' @param sequence string defining the MRCA sequence to seed mutations from. #' @param graph \code{igraph} object defining the seed lineage tree, with #' vertex annotations, whose edges are to be recreated. #' @param targetingModel 5-mer \link{TargetingModel} object to be used for computing #' probabilities of mutations at each position. Defaults to #' \link{HH_S5F}. #' @param field annotation to use for both unweighted path length exclusion #' and consideration as the MRCA node. If \code{NULL} do not #' exclude any nodes. #' @param exclude vector of annotation values in \code{field} to exclude from #' potential MRCA set. If \code{NULL} do not exclude any nodes. #' Has no effect if \code{field=NULL}. #' @param junctionWeight fraction of the nucleotide sequence that is within the #' junction region. When specified this adds a proportional #' number of mutations to the immediate offspring nodes of the #' MRCA. Requires a value between 0 and 1. If \code{NULL} then #' edge weights are unmodified from the input \code{graph}. #' @param start Initial position in \code{sequence} where mutations can #' be introduced. Default: 1 #' @param end Last position in \code{sequence} where mutations can #' be introduced. Default: last position (sequence length). #' @return A \code{data.frame} of simulated sequences with columns: #' \itemize{ #' \item \code{name}: name of the corresponding node in the input #' \code{graph}. #' \item \code{sequence}: mutated sequence. #' \item \code{distance}: Hamming distance of the mutated sequence from #' the seed \code{sequence}. #' } #' #' @seealso See \link{shmulateSeq} for imposing mutations on a single sequence. #' See \link{HH_S5F} and \link{MK_RS5NF} for predefined #' \link{TargetingModel} objects. #' #' @examples #' # Load example lineage and define example MRCA #' data(ExampleTrees, package="alakazam") #' graph <- ExampleTrees[[17]] #' sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATAGTTTA" #' #' # Simulate using the default human 5-mer targeting model #' shmulateTree(sequence, graph) #' #' # Simulate using the mouse 5-mer targeting model #' # Exclude nodes without a sample identifier #' # Add 20% mutation rate to the immediate offsprings of the MRCA #' shmulateTree(sequence, graph, targetingModel=MK_RS5NF, #' field="sample_id", exclude=NA, junctionWeight=0.2) #' #' @export shmulateTree <- function(sequence, graph, targetingModel=HH_S5F, field=NULL, exclude=NULL, junctionWeight=NULL, start=1, end=nchar(sequence)) { ## DEBUG # targetingModel=HH_S5F; field=NULL; exclude=NULL; junctionWeight=NULL # Check targeting model if (!is(targetingModel, "TargetingModel")) { stop(deparse(substitute(targetingModel)), " is not a valid TargetingModel object") } # Determine MRCA of lineage tree mrca_df <- alakazam::getMRCA(graph, path="distance", root="Germline", field=field, exclude=exclude) # Get adjacency matrix adj <- as_adjacency_matrix(graph, attr="weight", sparse=FALSE) # Get names of nodes for which sequences are not to be returned skip_names <- c() if (!is.null(field)) { g <- vertex_attr(graph, name=field) g_names <- vertex_attr(graph, name="name") skip_names <- g_names[g %in% exclude] } # Create data.frame to hold simulated sequences # this will include a row for Germline sim_tree <- data.frame(matrix(NA, ncol=3, nrow=length(V(graph)), dimnames=list(NULL, c("name", "sequence", "distance")))) sim_tree$name <- vertex_attr(graph, name="name") # remove row for Germline sim_tree <- sim_tree[-which(sim_tree$name=="Germline"), ] parent_nodes <- mrca_df$name[1] nchild <- sum(adj[parent_nodes, ] > 0) sim_tree$sequence[which(sim_tree$name==parent_nodes)] <- sequence sim_tree$distance[which(sim_tree$name==parent_nodes)] <- 0 # Add mutations to the immediate offsprings of the MRCA # Number of mutations added is proportional to fraction of sequence in junction if (!is.null(junctionWeight)) { adj[parent_nodes, ] <- round(adj[parent_nodes, ] * (1 + junctionWeight)) } while (nchild > 0) { new_parents <- c() # Loop through parent-children combos for(p in parent_nodes) { children <- colnames(adj)[adj[p, ] > 0] for(ch in children) { # Add child to new parents new_parents <- union(new_parents, ch) # Simulate sequence for that edge seq <- shmulateSeq(sequence=sim_tree$sequence[sim_tree$name == p], numMutations=adj[p, ch], targetingModel=targetingModel, start=start, end=end) # Update output data.frame chRowIdx = which(sim_tree$name==ch) sim_tree$sequence[chRowIdx] <- seq sim_tree$distance[chRowIdx] <- adj[p, ch] } } # Re-calculate number of children parent_nodes <- new_parents nchild <- sum(adj[parent_nodes, ] > 0) } # Remove sequences that are to be excluded sim_tree <- sim_tree[!(sim_tree$name %in% skip_names), ] # Remove NAs # e.g. if node B is an offspring of node A, and node A has been excluded # then node B will have $sequence and $distance of NAs sim_tree <- sim_tree[!is.na(sim_tree$sequence), ] rownames(sim_tree) <- NULL return(sim_tree) } #### Helper functions #### # Compute the mutations types # # For each position in the input sequence, use \code{CODON_TABLE} to # determine what types of mutations are possible. Returns \code{matrix} # of all possible mutations and corresponding types. # # @param inputSeq sequence for which to compute mutation types # @return A \code{matrix} of mutation types for each position in the sequence. computeMutationTypes <- function(inputSeq){ #* counts on constant variable CODON_TABLE, NUCLEOTIDES (ACTGN-.) #* caution: this breaks down if length of seq is not a multiple of 3 leng_seq <- stri_length(inputSeq) try(if( (leng_seq %%3 !=0) ) stop("length of input sequence must be a multiple of 3")) codons <- sapply(seq(1, leng_seq, by=3), function(x) {substr(inputSeq,x,x+2)}) unrecognized_codons <- codons[!codons %in% colnames(CODON_TABLE)] if (length(unrecognized_codons)>0) { if (all(grepl("^[[:lower:]]+$", unrecognized_codons))) { warning("shazam is case sensitive") } stop("Unrecognized codons found :\n", paste(unrecognized_codons, collapse="\n")) } mut_types <- matrix(unlist(CODON_TABLE[, codons]), ncol=leng_seq, nrow=4, byrow=F) dimnames(mut_types) <- list(NUCLEOTIDES[1:4], 1:leng_seq) return(mut_types) } # Pick a position to mutate # # Sample positions in the sequence to mutate given targeting probability # until a new position is selected. This new position is then added to the # vector of mutated positions and returned. # # @param sim_leng length of sequence in which mutation is being simulated # @param targeting probabilities of each position in the sequence being mutated # @param positions vector of positions which have already been mutated # # @return A \code{list} of mutation and position being mutated. sampleMut <- function(sim_leng, targeting, positions) { if (length(positions) > sim_leng ) { stop("The vector of positions is longer than the length of the sequence.") } pos <- 0 # Sample mutations until new position is selected while (pos %in% positions) { # Randomly select a mutation mut <- sample(1:(4*sim_leng), 1, replace=F, prob=as.vector(targeting)) pos <- ceiling(mut/4) } return(list(mut=mut, pos=pos)) } shazam/R/Shazam.R0000644000176200001440000002522614502023561013311 0ustar liggesusers#' @keywords internal #' @aliases shazam-package "_PACKAGE" # Shazam package documentation and import directives #' The shazam package #' #' Dramatic improvements in high-throughput sequencing technologies now enable #' large-scale characterization of Ig repertoires, defined as the collection of transmembrane #' antigen-receptor proteins located on the surface of T and B lymphocytes. The \code{shazam} #' package provides tools for advanced analysis of somatic hypermutation (SHM) in #' immunoglobulin (Ig) sequences. The key functions in \code{shazam}, broken down topic, are #' described below. #' #' @section Mutational profiling: #' \code{shazam} provides tools to quantify the extent and nature of SHM within #' full length V(D)J sequences as well as sub-regions (eg, FWR and CDR). #' Quantification of expected mutational loaded, under specific SHM targeting #' models, can also be performed along with model driven simulations of SHM. #' #' \itemize{ #' \item \link{collapseClones}: Build clonal consensus sequences. #' \item \link{consensusSequence}: Build a single consensus sequence. #' \item \link{observedMutations}: Compute observed mutation counts and frequencies. #' \item \link{expectedMutations}: Compute expected mutation frequencies. #' \item \link{shmulateSeq}: Simulate mutations in a single sequence. #' \item \link{shmulateTree}: Simulate mutations over a lineage tree. #' \item \link{setRegionBoundaries}: Extends a region definition to include CDR3 and FWR4. #' } #' #' @section SHM targeting models: #' Computational models and analyses of SHM have separated the process #' into two independent components: #' \enumerate{ #' \item A mutability model that defines where mutations occur. #' \item A nucleotide substitution model that defines the resulting mutation. #' } #' Collectively these are what form the targeting model of SHM. \code{shazam} #' provides empirically derived targeting models for both humans and mice, #' along with tools to build these mutability and substitution models from data. #' #' \itemize{ #' \item \link{createTargetingModel}: Build a 5-mer targeting model. #' \item \link{plotMutability}: Plot 5-mer mutability rates. #' \item \link{HH_S5F}: Human 5-mer SHM targeting model. #' \item \link{MK_RS5NF}: Mouse 5-mer SHM targeting model. #' } #' #' @section Quantification of selection pressure: #' Bayesian Estimation of Antigen-driven Selection in Ig Sequences is a #' novel method for quantifying antigen-driven selection in high-throughput #' Ig sequence data. Targeting models created using \code{shazam} can be used #' to estimate the null distribution of expected mutation frequencies used #' by BASELINe, providing measures of selection pressure informed by known #' AID targeting biases. #' #' \itemize{ #' \item \link{calcBaseline}: Calculate the BASELINe probability #' density functions (PDFs). #' \item \link{groupBaseline}: Combine PDFs from sequences grouped #' by biological or experimental relevance. #' \item \link{summarizeBaseline}: Compute summary statistics from BASELINe PDFs. #' \item \link{testBaseline}: Perform significance testing for the difference #' between BASELINe PDFs. #' \item \link{plotBaselineDensity}: Plot the probability density functions #' resulting from selection analysis. #' \item \link{plotBaselineSummary}: Plot summary stastistics resulting from #' selection analysis. #' } #' #' @section Mutational distance calculation: #' \code{shazam} provides tools to compute evolutionary distances between #' sequences or groups of sequences, which can leverage SHM targeting #' models. This information is particularly useful in understanding and #' defining clonal relationships. #' #' \itemize{ #' \item \link{findThreshold}: Identify clonal assignment threshold based on #' distances to nearest neighbors. #' \item \link{distToNearest}: Tune clonal assignment thresholds by calculating #' distances to nearest neighbors. #' \item \link{calcTargetingDistance}: Construct a nucleotide distance matrix from a #' 5-mer targeting model. #' } #' #' @name shazam #' @docType package #' @references #' \enumerate{ #' \item Hershberg U, et al. Improved methods for detecting selection by mutation #' analysis of Ig V region sequences. #' Int Immunol. 2008 20(5):683-94. #' \item Uduman M, et al. Detecting selection in immunoglobulin sequences. #' Nucleic Acids Res. 2011 39(Web Server issue):W499-504. (Corrections at #' http://selection.med.yale.edu/baseline/correction/) #' \item Yaari G, et al. Quantifying selection in high-throughput immunoglobulin #' sequencing data sets. #' Nucleic Acids Res. 2012 40(17):e134. #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4:358. #' \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, #' Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation #' Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of #' Immunology, 197(9), 3566-3574. #' } #' #' @import ggplot2 #' @import graphics #' @import methods #' @import utils #' @importFrom alakazam getAllele getGene getFamily getSegment getLocus groupGenes #' getAAMatrix getDNAMatrix IUPAC_DNA #' pairwiseDist nonsquareDist pairwiseEqual #' seqDist seqEqual #' isValidAASeq translateStrings gridPlot #' getMRCA getPathLengths tableEdges #' progressBar baseTheme checkColumns cpuCount #' makeChangeoClone summarizeSubtrees buildPhylipLineage #' @importFrom ape mst #' @importFrom diptest dip.test #' @importFrom doParallel registerDoParallel #' @importFrom dplyr do n desc %>% #' bind_cols bind_rows combine #' filter select arrange #' group_by ungroup group_indices #' mutate summarize #' mutate_at summarize_at #' rename transmute #' left_join recode pull #' @importFrom foreach foreach %dopar% registerDoSEQ #' @importFrom igraph V E as_adjacency_matrix graph_from_data_frame #' vertex_attr set_vertex_attr #' layout_as_tree V<- #' @importFrom iterators icount #' @importFrom KernSmooth bkde #' @importFrom lazyeval interp #' @importFrom MASS fitdistr #' @importFrom progress progress_bar #' @importFrom rlang sym syms .data #' @importFrom scales log2_trans log10_trans trans_breaks trans_format #' math_format percent scientific pretty_breaks #' @importFrom seqinr c2s s2c words translate #' @importFrom stats na.omit setNames ecdf sd cor cov median mad #' approx convolve weighted.mean p.adjust #' dbeta pbeta qbeta rbeta optim optimize #' dnorm pnorm runif dgamma pgamma uniroot na.exclude #' as.dist cutree integrate #' @importFrom stringi stri_dup stri_flatten stri_join stri_length #' stri_sub stri_sub_replace stri_detect_regex #' stri_count_boundaries stri_count_regex #' stri_extract_all_regex stri_extract_first_regex #' stri_replace_all_regex stri_replace_first_regex #' @importFrom tidyr gather spread pivot_wider #' @importFrom tidyselect all_of any_of NULL # Package loading actions .onAttach <- function(libname, pkgname) { msg <- citation(pkgname) msg <-paste(c(format(msg,"citation")),collapse="\n\n") packageStartupMessage(msg) } #### Sysdata #### # Deprecated (v0.1.4) mouse single nucleotide distance matrix # # Single nucleotide distance matrix of somatic hypermutation targeting based on # Mus musculus Ig sequence data. # # @format A symmetric matrix of nucleotide substitution distance scores with # row names and column names definition the specific subsitution. # # @references # \enumerate{ # \item Smith DS, et al. Di- and trinucleotide target preferences of somatic # mutagenesis in normal and autoreactive B cells. # J Immunol. 1996 156:2642-52. # } # # M1N_Compat # Deprecated (v0.1.4) Human single nucleotide distance matrix. # # Single nucleotide distance matrix of somatic hypermutation targeting based on # human Ig sequence data. # # @format A symmetric matrix of nucleotide substitution distance scores with # row names and column names definition the specific subsitution. # # @references # \enumerate{ # \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based # on synonymous mutations from high-throughput immunoglobulin sequencing data. # Front Immunol. 2013 4(November):358. # } # # HS1F_Compat # Ordered nucleotide character set # NUCLEOTIDES <- c("A", "C", "G", "T", "N", "-", ".") # IMGT V segment length # VLENGTH <- 312 # 5x312 logical matrix of CDR positions # CDR_Nuc_Mat # 5x312 logical matrix of FWR positions # FWR_Nuc_Mat # 12x216 matrix of replacement and silent mutation permutations # CODON_TABLE # 1x24 vector of amino acid charge classes # AMINO_ACIDS_CHARGE # 1x24 vector of amino acid hydropathy classes # AMINO_ACIDS_HYDROPATHY # 1x24 vector of amino acid polarity classes # AMINO_ACIDS_POLARITY # TODO: What is this? # CONST_I # TODO: And what is this? # BAYESIAN_FITTED # IMGT-KABAT numbering mapping # As described hare http://www.imgt.org/IMGTScientificChart/Numbering/IMGT-Kabat_part1.html # CONVERT_NUM_REF # Add built-in variables to global variables environment utils::globalVariables(c("HH_S1F", "HKL_S1F", "MK_RS1NF", "HH_S5F", "HKL_S5F", "MK_RS5NF", "U5N", "IMGT_V_BY_REGIONS"), package="shazam")shazam/R/DistToNearest.R0000644000176200001440000027137514506565410014636 0ustar liggesusers# Generates distance to nearest neighbor #' @include Shazam.R #' @include Core.R NULL #### Classes #### #' Output of the \code{gmm} method of findThreshold #' #' \code{GmmThreshold} contains output from the \code{gmm} method \link{findThreshold}. #' It includes parameters of two Gaussian fits and threshold cut. #' #' @slot x input distance vector with NA or infinite values removed. #' @slot model first-second fit functions. #' @slot cutoff type of threshold cut. #' @slot a1 mixing weight of the first curve. #' @slot b1 second parameter of the first curve. Either the mean of a Normal #' distribution or shape of a Gamma distribution. #' @slot c1 third parameter of the first curve. Either the standard deviation of a #' Normal distribution or scale of a Gamma distribution. #' @slot a2 mixing weight of the second curve. #' @slot b2 second parameter of the second curve. Either the mean of a Normal #' distribution or shape of a Gamma distribution. #' @slot c2 third parameter of the second curve. Either the standard deviation #' of a Normal distribution or scale of a Gamma distribution. #' @slot loglk log-likelihood of the fit. #' @slot threshold threshold. #' @slot sensitivity sensitivity. #' @slot specificity specificity. #' @slot pvalue p-value from Hartigans' dip statistic (HDS) test. #' Values less than 0.05 indicate significant bimodality. #' #' @seealso \link{findThreshold} #' #' @name GmmThreshold-class #' @rdname GmmThreshold-class #' @aliases GmmThreshold #' @exportClass GmmThreshold setClass("GmmThreshold", slots=c(x="numeric", model = "character", cutoff = "character", a1="numeric", b1="numeric", c1="numeric", a2="numeric", b2="numeric", c2="numeric", loglk="numeric", threshold="numeric", sensitivity="numeric", specificity="numeric", pvalue="numeric")) #' Output of the \code{dens} method of findThreshold #' #' \code{DensityThreshold} contains output from the \code{dens} method \link{findThreshold}. #' #' @slot x input distance vector with NA or infinite values removed. #' @slot bandwidth bandwidth value fit during density estimation. #' @slot xdens x-axis (distance value) vector for smoothed density estimate. #' @slot ydens y-axis (density) vector for smoothed density estimate. #' @slot threshold distance threshold that separates two modes of the input distribution. #' #' @seealso \link{findThreshold} #' #' @name DensityThreshold-class #' @rdname DensityThreshold-class #' @aliases DensityThreshold #' @exportClass DensityThreshold setClass("DensityThreshold", slots=c(x="numeric", bandwidth="numeric", xdens="numeric", ydens="numeric", threshold="numeric")) #### Methods #### #' @param x GmmThreshold object #' #' @rdname GmmThreshold-class #' @aliases GmmThreshold-method #' @export setMethod("print", c(x="GmmThreshold"), function(x) { print(x@threshold) }) #' @param y ignored. #' @param ... arguments to pass to \link{plotGmmThreshold}. #' #' @rdname GmmThreshold-class #' @aliases GmmThreshold-method #' @export setMethod("plot", c(x="GmmThreshold", y="missing"), function(x, y, ...) { plotGmmThreshold(x, ...) }) #' @param x DensityThreshold object #' #' @rdname DensityThreshold-class #' @aliases DensityThreshold-method #' @export setMethod("print", c(x="DensityThreshold"), function(x) { print(x@threshold) }) #' @param y ignored. #' @param ... arguments to pass to \link{plotDensityThreshold}. #' #' @rdname DensityThreshold-class #' @aliases DensityThreshold-method #' @export setMethod("plot", c(x="DensityThreshold", y="missing"), function(x, y, ...) { plotDensityThreshold(x, ...) }) #### Distance to Nearest #### # Returns a 5-mer sliding window of given sequence # # @param sequence sequence string # @return An array of 5-mer sliding windows # # @examples # window5Mers("ACGTNACGTNACGTN") window5Mers <- function(sequence) { n <- stri_length(sequence) w <- substr(rep(sequence, n - 4), 1:(n - 4), 5:n) return(w) } # Get distance between two sequences of same length, broken by a sliding window of 5mers # # @param seq1 first nucleotide sequence, broken into 5mers. # @param seq2 second nucleotide sequence, broken into 5mers. # @param targetingDistance targeting distance obtained from a targeting model # with the function \code{calcTargetingDistance}. # @param symmetry if model is hs5f, distance between seq1 and seq2 is either # the average (avg) of seq1->seq2 and seq2->seq1 or the # minimum (min). # @return distance between two sequences. # # @examples # seq1 <- c("NNACG", "NACGT", "ACGTA", "CGTAC", "GTACG", "TACGT", "ACGTA", # "CGTAC", "GTACG", "TACGT", "ACGTN", "CGTNN") # seq2 <- c("NNACG", "NACGA", "ACGAA", "CGAAC", "GAACG", "AACGT", "ACGTA", # "CGTAC", "GTACG", "TACGT", "ACGTN", "CGTNN") # targeting_distance <- calcTargetingDistance(HH_S5F) # shazam:::dist5Mers(seq1, seq2, targeting_distance) dist5Mers <- function(seq1, seq2, targetingDistance, symmetry=c("avg", "min", "raw")) { # Evaluate choices symmetry <- match.arg(symmetry) # Get distance from targeting model #targeting_dist <- calcTargetingDistance(targetingModel) # Check all characters in seq1 and seq2 are valid, # found in the targetingModel distance matrix validChars <- rownames(targetingDistance) allChars <- unique(strsplit(paste(c(seq1, seq2), collapse=""), "")[[1]]) invalidChars <- allChars[allChars %in% validChars == F] if (length(invalidChars) > 0 ) { stop(paste0("Character not found in targeting_dist: ", paste(invalidChars, collapse=", "))) } # Compute distance only on fivemers that have mutations fivemersWithMu <- substr(seq1, 3, 3) != substr(seq2, 3, 3) #fivemersWithNonNuc <- (!is.na(match(substr(seq1,3,3),c("A","C","G","T"))) & # !is.na(match(substr(seq2,3,3),c("A","C","G","T")))) #fivemersWithMu <- fivemersWithMu & fivemersWithNonNuc seq1 <- seq1[fivemersWithMu] seq2 <- seq2[fivemersWithMu] # Number of mutations (for normalization, if specified) #numbOfMutation <- sum(fivemersWithMu) dist <- NA tryCatch({ if (length(seq1)==1){ seq1_to_seq2 <- targetingDistance[substr(seq2, 3, 3), seq1] seq2_to_seq1 <- targetingDistance[substr(seq1, 3, 3), seq2] } else { seq1_to_seq2 <- diag(targetingDistance[substr(seq2, 3, 3), seq1]) seq2_to_seq1 <- diag(targetingDistance[substr(seq1, 3, 3), seq2]) } if (symmetry == "avg") { dist <- sum(apply(cbind(seq1_to_seq2, seq2_to_seq1), 1, mean)) } else if (symmetry == "min") { dist <- sum(apply(cbind(seq1_to_seq2, seq2_to_seq1), 1, min)) } else if (symmetry == "raw") { dist <- c(seq1_to_seq2, seq2_to_seq1) } }, error = function(e) { warning(e) return(NA) }) return(dist) } # Given an array of nucleotide sequences, find the pairwise distances # # @param sequences character vector of nucleotide sequences. # @param targetingDistance targeting distance obtained from a targeting model # with the function `calcTargetingDistance` # @param symmetry if model is hs5f, distance between seq1 and seq2 is either the # average (avg) of seq1->seq2 and seq2->seq1 or the minimum (min). # # @return A matrix of pairwise distances between junction sequences. pairwise5MerDist <- function(sequences, targetingDistance, symmetry=c("avg", "min")) { # get names seq_names <- names(sequences) # Initial checks symmetry <- match.arg(symmetry) # Convert junctions to uppercase sequences <- toupper(sequences) # Convert gaps to Ns sequences <- gsub('[-.]', 'N', sequences, fixed=T) # Add 'NN' to front and end of each sequence for fivemers sequences <- as.vector(sapply(sequences, function(x){ paste("NN", x, "NN", sep="") })) n_seq <- length(sequences) #Junctions are broken in to 5-mers based on a sliding window (of one) and placed in matrix #Each column is a junction #E.g. junctions 1234567, ABCDEFG, JKLMNOP becomes: # 12345 ABCDE JKLMN # 23456 BCDEF KLMNO # 34567 CDEFG LMNOP .matSeqSlidingFiveMer <- sapply(sequences, function(x) { window5Mers(x) }, simplify="matrix") # Compute pairwise distance between all sequences' fivemers (by column) .dist <- function(i) { c(rep.int(0, i - 1), sapply(i:n_seq, function(j) { dist5Mers(.matSeqSlidingFiveMer[,i], .matSeqSlidingFiveMer[,j], targetingDistance, symmetry=symmetry) })) } dist_mat <- sapply(1:n_seq, .dist) # Make distance matrix symmetric dist_mat <- dist_mat + t(dist_mat) # assign names if (!is.null(seq_names)) { rownames(dist_mat) <- seq_names colnames(dist_mat) <- seq_names } return(dist_mat) } # Given an array of nucleotide sequences and a vector indices (a subset of array of nucleotide sequences), # find the pairwise distances # # @param sequences character vector of nucleotide sequences. # @paramindx numeric vector of subsamples indices # @param targetingDistance targeting distance obtained from a targeting model # with the function `calcTargetingDistance` # @param symmetry if model is hs5f, distance between seq1 and seq2 is either the # average (avg) of seq1->seq2 and seq2->seq1 or the minimum (min). # # @return A non-square matrix of pairwise distances between junction sequences. nonsquare5MerDist <- function(sequences, indx, targetingDistance, symmetry=c("avg", "min")) { # get names seq_names <- names(sequences) # Initial checks symmetry <- match.arg(symmetry) # Convert junctions to uppercase sequences <- toupper(sequences) # Convert gaps to Ns sequences <- gsub('[-.]', 'N', sequences, fixed=T) # Add 'NN' to front and end of each sequence for fivemers sequences <- as.vector(sapply(sequences, function(x){ paste("NN", x, "NN", sep="") })) n_seq <- length(sequences) #Junctions are broken in to 5-mers based on a sliding window (of one) and placed in matrix #Each column is a junction #E.g. junctions 1234567, ABCDEFG, JKLMNOP becomes: # 12345 ABCDE JKLMN # 23456 BCDEF KLMNO # 34567 CDEFG LMNOP .matSeqSlidingFiveMer <- sapply(sequences, function(x) { window5Mers(x) }, simplify="matrix") # # Compute pairwise distance between all sequences' fivemers (by column) # .dist <- function(i) { # d <- c(rep.int(0, i - 1), # sapply(i:n_seq, function(j) { dist5Mers(.matSeqSlidingFiveMer[,i], # .matSeqSlidingFiveMer[,j], # targetingDistance, # symmetry=symmetry) })) # } dist_mat <- matrix(NA, nrow=n_seq, ncol=n_seq) diag(dist_mat) <- 0 indx <- sort(indx) for (i in 1:n_seq) { if (!(i %in% indx)) next for (j in 1:n_seq) { if (!is.na(dist_mat[i,j])) next dist_mat[i,j] = dist5Mers(.matSeqSlidingFiveMer[,i], .matSeqSlidingFiveMer[,j], targetingDistance, symmetry=symmetry) dist_mat[j,i] = dist_mat[i,j] } } sub_dist_mat <- dist_mat[indx,] # assign names if (!is.null(seq_names)) { rownames(sub_dist_mat) <- seq_names[indx] colnames(sub_dist_mat) <- seq_names } return(sub_dist_mat) } # Subset to unique sequences # # @param sequences character vector of sequences # # @return Named vector of unique sequences, with names as the sequence itself. findUniqSeq <- function(sequences) { seq_uniq <- unique(sequences) names(seq_uniq) <- seq_uniq return(seq_uniq) } # Get chars in the distance model # # @param model # # @return vector of unique chars in the distance model # @examples # getCharsInModel("hh_s1f") getCharsInModel <- function(model) { if (model == "ham") { chars <- colnames(getDNAMatrix(gap=0)) } else if (model == "aa") { chars <- colnames(getAAMatrix()) } else if (model == "hh_s1f") { chars <- colnames(HH_S1F_Distance) } else if (model == "hh_s5f") { chars <-rownames(HH_S5F@targeting) } else if (model == "mk_rs1nf") { chars <- colnames(MK_RS1NF_Distance) } else if (model == "mk_rs5nf") { chars <-rownames(MK_RS1NF@targeting) } else if (model == "hs1f_compat") { chars <- colnames(HS1F_Compat) } else if (model == "m1n_compat") { chars <- colnames(M1N_Compat) } return(chars) } # Validate the sequence # # @param seq # @param validChars # # @return TRUE is all the character in the sequence are found in validChars; # FALSE otherwise # @examples # allValidChars("ATCG", getCharsInModel("hh_s1f")) # allValidChars("ATCG.", getCharsInModel("hh_s1f")) # allValidChars("ATCGJ", getCharsInModel("hh_s1f")) allValidChars <- function(seq, validChars) { all(unique(strsplit(seq, "")[[1]]) %in% validChars) } # Given an array of sequences, find the distance to the closest sequence # # @param sequences character vector of sequences. # @param model 5-mer or 1-mer distance model # @param normalize method of normalization. Default is "none". # "len" = normalize distance by length of junction. # "mut" = normalize distance by number of mutations in # junction. # @param symmetr if model is hs5f or mrs5nf, distance between seq1 and seq2 is either the # average (avg) of seq1->seq2 and seq2->seq1 or the minimum (min). # @param crossGroups column for grouping to calculate distances across groups # (self vs others). # @param mst if true, return comma-separated branch lengths from minimum # spanning tree. # # @return A vector of distances to the closest sequence. # # @examples # sequences <- c("ACGTACGTACGT", "ACGAACGTACGT", "ACGAACGTATGT", "ACGAACGTATGC", # "ACGAACGTATCC", "AAAAAAAAAAAA", "A-GAACGTATCC", "AAAAAA---AAA") # shazam:::nearestDist(sequences, model="ham", normalize="none") # shazam:::nearestDist(sequences, model="aa", normalize="none") # shazam:::nearestDist(sequences, model="ham", normalize="len") # shazam:::nearestDist(sequences, model="aa", normalize="len") nearestDist <- function(sequences, model=c("ham", "aa", "hh_s1f", "hh_s5f", "mk_rs1nf", "mk_rs5nf", "hs1f_compat", "m1n_compat"), normalize=c("none", "len", "mut"), symmetry=c("avg", "min"), crossGroups=NULL, mst=FALSE, subsample=NULL) { ## DEBUG # sequences <- c("ACGTACGTACGT", "ACGAACGTACGT", "AAAAAAAAAAAA", "A-AAAA---AAA") # model="aa"; normalize="len"; crossGroups=NULL; mst=FALSE # Initial checks model <- match.arg(model) normalize <- match.arg(normalize) ## If crossGroup requested, but only one group found, return NA if (!is.null(crossGroups) & length(unique(crossGroups)) < 2) { seq_dist <- rep(NA, length(sequences)) return (seq_dist) } # Find unique sequences seq_uniq <- findUniqSeq(sequences) n_uniq <- length(seq_uniq) # corresponding crossGroups values for seq_uniq if (!is.null(crossGroups)) { stopifnot( all.equal(sequences[match(seq_uniq, sequences)], seq_uniq, check.attributes=FALSE) ) crossGroups_uniq <- crossGroups[match(seq_uniq, sequences)] } # Initialize return vector and computation vector seq_dist <- setNames(rep(NA, length(sequences)), sequences) seq_uniq_dist <- rep(NA, n_uniq) # Compute distances between sequences if (n_uniq > 1) { # Check for length mismatches seq_length <- unique(stri_length(seq_uniq)) if (length(seq_length) > 1) { stop("Unexpected. Different sequence lengths found.") } # check subSampling subSampling <- all(!is.null(subsample), subsample < n_uniq) if (subSampling) indx <- sample(x=1:n_uniq, size=subsample, replace=FALSE, prob=NULL) # corresponding subsampling of crossGroups_uniq if (subSampling & !is.null(crossGroups)) { crossGroups_uniq_sub <- crossGroups_uniq[indx] } # Get distance matrix if (model == "ham") { if (subSampling) { dist_mat <- nonsquareDist(seq_uniq, indx, dist_mat=getDNAMatrix(gap=0)) } else { dist_mat <- pairwiseDist(seq_uniq, dist_mat=getDNAMatrix(gap=0)) } } else if (model == "aa") { seq_uniq <- setNames(alakazam::translateDNA(seq_uniq), seq_uniq) if (subSampling) { dist_mat <- nonsquareDist(seq_uniq, indx, dist_mat=getAAMatrix()) } else { dist_mat <- pairwiseDist(seq_uniq, dist_mat=getAAMatrix()) } } else if (model == "hh_s1f") { if (subSampling) { dist_mat <- nonsquareDist(seq_uniq, indx, dist_mat=HH_S1F_Distance) } else { dist_mat <- pairwiseDist(seq_uniq, dist_mat=HH_S1F_Distance) } } else if (model == "mk_rs1nf") { if (subSampling) { dist_mat <- nonsquareDist(seq_uniq, indx, dist_mat=MK_RS1NF_Distance) } else { dist_mat <- pairwiseDist(seq_uniq, dist_mat=MK_RS1NF_Distance) } } else if (model == "hh_s5f") { if (subSampling) { dist_mat <- nonsquare5MerDist(seq_uniq, indx, HH_S5F_Distance, symmetry=symmetry) } else { dist_mat <- pairwise5MerDist(seq_uniq, HH_S5F_Distance, symmetry=symmetry) } } else if (model == "mk_rs5nf") { if (subSampling) { dist_mat <- nonsquare5MerDist(seq_uniq, indx, MK_RS5NF_Distance, symmetry=symmetry) } else { dist_mat <- pairwise5MerDist(seq_uniq, MK_RS5NF_Distance, symmetry=symmetry) } } else if (model == "hs1f_compat") { if (subSampling) { dist_mat <- nonsquareDist(seq_uniq, indx, dist_mat=HS1F_Compat) } else { dist_mat <- pairwiseDist(seq_uniq, dist_mat=HS1F_Compat) } } else if (model == "m1n_compat") { if (subSampling) { dist_mat <- nonsquareDist(seq_uniq, indx, dist_mat=M1N_Compat) } else { dist_mat <- pairwiseDist(seq_uniq, dist_mat=M1N_Compat) } } ## DEBUG # cat("\n-> seq_uniq:\n") # print(seq_uniq) # cat("\n-> dist_mat (raw):\n") # print(dist_mat) # Normalize distances if (normalize == "len") { dist_mat <- dist_mat / seq_length } else if (normalize == "mut") { #dist <- dist/sum(strsplit(seq1,"")[[1]] != strsplit(seq2,"")[[1]]) stop('Sorry! nomalize="mut" is not available.') } ## DEBUG # cat("\n-> seq_length:\n") # print(seq_length) # cat("\n-> dist_mat (normalized):\n") # print(dist_mat) } else { return(seq_dist) } # Find minimum distance for each sequence if (is.null(crossGroups)) { if(!mst) { # Return smaller value greater than 0 # If all 0, return NA .dmin <- function(i) { x <- dist_mat[, i] gt0 <- which(x > 0) if (length(gt0) != 0) { min(x[gt0]) } else { NA } } ## TODO: Could be an apply over columns seq_uniq_dist <- setNames(sapply(1:n_uniq, .dmin), names(seq_uniq)) } else { # Get adjacency matrix of minimum spanning tree adj <- ape::mst(dist_mat) # TODO: This could be cleaner # Get value(s) from mst branches # If none (broken mst!), return NA # If multiple values, comma-join .dmst <- function(i) { gt0 <- which(adj[, i] == 1) if (length(gt0) != 0) { stri_join(round(dist_mat[, i][gt0], 4), collapse=",") } else { NA } } ## TODO: Could be an apply over columns seq_uniq_dist <- setNames(sapply(1:n_uniq, .dmst), names(seq_uniq)) } # Define return distance vector seq_dist <- seq_uniq_dist[match(names(seq_dist), names(seq_uniq_dist))] ## DEBUG # cat("\n-> seq_uniq_dist:\n") # print(seq_uniq_dist) # cat("\n-> seq_dist:\n") # print(seq_dist) } else { # Identify sequences to be considered when finding minimum # cross distance .dcross <- function(i) { #cat(i,"\n") this_group <- crossGroups[i] other_groups <- which(crossGroups != this_group) other_seq <- unique(sequences[other_groups]) if (model=="aa") { seq_uniq <- names(seq_uniq) } other_idx <- match(other_seq, seq_uniq) this_idx <- match(sequences[i], seq_uniq) stopifnot( all.equal( other_seq, seq_uniq[other_idx] , check.attributes=FALSE ) ) stopifnot( all.equal( sequences[i], seq_uniq[this_idx] , check.attributes=FALSE ) ) # the next two checks may not always be true # this happens when all the out-group sequences are identical to the in-group sequences #stopifnot( all( crossGroups_uniq[other_idx] != this_group ) ) #stopifnot( crossGroups_uniq[this_idx] == this_group ) if (subSampling) { # When there is subsampling, nonsquareDist returns a non-n-by-n matrix # This matrix has fewers than n rows, and exactly n cols # For each unique sequence, look for its cross-group distances in its column, # NOT in its row (because there will be fewer than n rows) # dist_mat rows correspond to seq_uniq[indx] # (indx itself is wrt seq_uniq) # (other_idx is also wrt seq_uni) # which other_seq are included in the subsampled seqs represented by # the available rows in dist_mat? # wrt dist_mat other_avail_wrt_dist_mat <- which(indx %in% other_idx) if (length(other_avail_wrt_dist_mat)>0) { # the next two checks may not always be true # this happens when all the out-group sequences are identical to the in-group sequences #stopifnot(all( crossGroups_uniq_sub[other_avail_wrt_dist_mat] != this_group )) #stopifnot(all( crossGroups_uniq_sub[-other_avail_wrt_dist_mat] == this_group )) r <- dist_mat[other_avail_wrt_dist_mat, this_idx] } else { stopifnot(all( crossGroups_uniq_sub == this_group )) return(NA) } } else { # without subsampling # dist_mat is a n-by-n matrix stopifnot( all(other_idx <= nrow(dist_mat) ) ) r <- dist_mat[other_idx, this_idx] } gt0 <- which(r > 0) if (length(gt0) != 0) { return(min(r[gt0])) } else { return(NA) } } # Define return distance vector seq_dist <- setNames(sapply(1:length(sequences), .dcross), sequences) } return(round(seq_dist, 4)) } #' Distance to nearest neighbor #' #' Get non-zero distance of every heavy chain (\code{IGH}) sequence (as defined by #' \code{sequenceColumn}) to its nearest sequence in a partition of heavy chains sharing the same #' V gene, J gene, and junction length (V-J-length), or in a partition of single cells with heavy/long chains #' sharing the same heavy/long chain V-J-length combination, or of single cells with heavy/long and light/short chains #' sharing the same heavy/long chain V-J-length and light/short chain V-J-length combinations. #' #' @param db data.frame containing sequence data. #' @param sequenceColumn name of the column containing the junction for grouping and for calculating #' nearest neighbor distances. Note that while both heavy/long and light/short chain junctions #' may be used for V-J-length grouping, only the heavy/long chain (IGH, TRB, TRD) junction is #' used to calculate distances. #' @param vCallColumn name of the column containing the V-segment allele calls. #' @param jCallColumn name of the column containing the J-segment allele calls. #' @param model underlying SHM model, which must be one of #' \code{c("ham", "aa", "hh_s1f", "hh_s5f", "mk_rs1nf", "hs1f_compat", "m1n_compat")}. #' See Details for further information. #' @param normalize method of normalization. The default is \code{"len"}, which #' divides the distance by the length of the sequence group. If #' \code{"none"} then no normalization if performed. #' @param symmetry if model is hs5f, distance between seq1 and seq2 is either the #' average (avg) of seq1->seq2 and seq2->seq1 or the minimum (min). #' @param first if \code{TRUE} only the first call of the gene assignments #' is used. if \code{FALSE} the union of ambiguous gene #' assignments is used to group all sequences with any #' overlapping gene calls. #' @param VJthenLen logical value specifying whether to perform partitioning as a 2-stage #' process. If \code{TRUE}, partitions are made first based on V and J #' gene, and then further split based on junction lengths corresponding #' to \code{sequenceColumn}. If \code{FALSE}, perform partition as a 1-stage #' process during which V gene, J gene, and junction length are used #' to create partitions simultaneously. Defaults to \code{TRUE}. #' @param nproc number of cores to distribute the function over. #' @param fields additional fields to use for grouping. #' @param cross character vector of column names to use for grouping to calculate #' distances across groups. Meaning the columns that define self versus others. #' @param mst if \code{TRUE}, return comma-separated branch lengths from minimum #' spanning tree. #' @param subsample number of sequences to subsample for speeding up pairwise-distance-matrix calculation. #' Subsampling is performed without replacement in each V-J-length group of heavy chain sequences. #' If \code{subsample} is larger than the unique number of heavy chain sequences in each #' VJL group, then the subsampling process is ignored for that group. For each heavy chain #' sequence in \code{db}, the reported \code{dist_nearest} is the distance to the closest #' heavy chain sequence in the subsampled set for the V-J-length group. If \code{NULL} no #' subsampling is performed. #' @param progress if \code{TRUE} print a progress bar. #' @param cellIdColumn name of the character column containing cell identifiers or barcodes. #' If specified, grouping will be performed in single-cell mode #' with the behavior governed by the \code{locusColumn} and #' \code{onlyHeavy} arguments. If set to \code{NULL} then the #' bulk sequencing data is assumed. #' @param locusColumn name of the column containing locus information. #' Valid loci values #' are "IGH", "IGI", "IGK", "IGL", "TRA", "TRB", #' "TRD", and "TRG". #' @param locusValues Loci values to focus the analysis on. #' @param onlyHeavy use only the IGH (BCR) or TRB/TRD (TCR) sequences #' for grouping. Only applicable to single-cell data. #' Ignored if \code{cellIdColumn=NULL}. #' See \link[alakazam]{groupGenes} for further details. #' @param keepVJLgroup logical value specifying whether to keep in the output the the column #' column indicating grouping based on V-J-length combinations. Only applicable for #' 1-stage partitioning (i.e. \code{VJthenLen=FALSE}). Also see #' \link[alakazam]{groupGenes}. #' #' @return Returns a modified \code{db} data.frame with nearest neighbor distances between heavy chain #' sequences in the \code{dist_nearest} column if \code{cross=NULL}. If \code{cross} was #' specified, distances will be added as the \code{cross_dist_nearest} column. #' #' Note that distances between light/short (IGK, IGL, TRA, TRG) chain sequences are not calculated, #' even if light/short chains were used for V-J-length grouping via \code{onlyHeavy=FALSE}. #' Light/short chain sequences, if any, will have \code{NA} in the \code{dist_nearest} output column. #' #' Note that the output \code{vCallColumn} and \code{jCallColumn} columns will be converted to #' type \code{character} if they were type \code{factor} in the input \code{db}. #' #' @details #' To invoke single-cell mode the \code{cellIdColumn} argument must be specified and \code{locusColumn} #' must be correct. Otherwise, \code{distToNearest} will be run with bulk sequencing assumptions, #' using all input sequences regardless of the values in the \code{locusColumn} column. #' #' Under single-cell mode, only heavy/long chain (IGH, TRB, TRD) sequences will be used for calculating #' nearest neighbor distances. Under non-single-cell mode, all input sequences will be used for #' calculating nearest neighbor distances, regardless of the values in the \code{locusColumn} field (if present). #' #' Values in the \code{locusColumn} must be one of \code{c("IGH", "IGI", "IGK", "IGL")} for BCR #' or \code{c("TRA", "TRB", "TRD", "TRG")} for TCR sequences. Otherwise, the function returns an #' error message and stops. #' #' For single-cell mode, the input format is the same as that for \link[alakazam]{groupGenes}. #' Namely, each row represents a sequence/chain. Sequences/chains from the same cell are linked #' by a cell ID in the \code{cellIdColumn} field. In this mode, there is a choice of whether #' grouping should be done by (a) using IGH (BCR) or TRB/TRD (TCR) sequences only or #' (b) using IGH plus IGK/IGL (BCR) or TRB/TRD plus TRA/TRG (TCR). #' This is governed by the \code{onlyHeavy} argument. #' #' Note, \code{distToNearest} required that each cell (each unique value in \code{cellIdColumn}) #' correspond to only a single \code{IGH} (BCR) or \code{TRB/TRD} (TCR) sequence. #' #' The distance to nearest neighbor can be used to estimate a threshold for assigning #' Ig sequences to clonal groups. A histogram of the resulting vector is often bimodal, with the #' ideal threshold being a value that separates the two modes. #' #' The following distance measures are accepted by the \code{model} parameter. #' #' \itemize{ #' \item \code{"ham"}: Single nucleotide Hamming distance matrix from \link[alakazam]{getDNAMatrix} #' with gaps assigned zero distance. #' \item \code{"aa"}: Single amino acid Hamming distance matrix from \link[alakazam]{getAAMatrix}. #' \item \code{"hh_s1f"}: Human single nucleotide distance matrix derived from \link{HH_S1F} with #' \link{calcTargetingDistance}. #' \item \code{"hh_s5f"}: Human 5-mer nucleotide context distance matix derived from \link{HH_S5F} with #' \link{calcTargetingDistance}. #' \item \code{"mk_rs1nf"}: Mouse single nucleotide distance matrix derived from \link{MK_RS1NF} with #' \link{calcTargetingDistance}. #' \item \code{"mk_rs5nf"}: Mouse 5-mer nucleotide context distance matrix derived from \link{MK_RS1NF} with #' \link{calcTargetingDistance}. #' \item \code{"hs1f_compat"}: Backwards compatible human single nucleotide distance matrix used in #' SHazaM v0.1.4 and Change-O v0.3.3. #' \item \code{"m1n_compat"}: Backwards compatibley mouse single nucleotide distance matrix used in #' SHazaM v0.1.4 and Change-O v0.3.3. #' } #' #' Note on \code{NA}s: if, for a given combination of V gene, J gene, and junction length, #' there is only 1 heavy chain sequence (as defined by \code{sequenceColumn}), \code{NA} is #' returned instead of a distance (since it has no heavy/long chain neighbor). If for a given combination #' there are multiple heavy/long chain sequences but only 1 unique one, (in which case every heavy/long cahin #' sequence in this group is the de facto nearest neighbor to each other, thus giving rise to distances #' of 0), \code{NA}s are returned instead of zero-distances. #' #' Note on \code{subsample}: Subsampling is performed independently in each V-J-length group for #' heavy/long chain sequences. If \code{subsample} is larger than number of heavy/long chain sequences #' in the group, it is ignored. In other words, subsampling is performed only on groups in which the #' number of heavy/long chain sequences is equal to or greater than \code{subsample}. \code{dist_nearest} #' has values calculated using all heavy chain sequences in the group for groups with fewer than #' \code{subsample} heavy/long chain sequences, and values calculated using a subset of heavy/long chain #' sequences for the larger groups. To select a value of \code{subsample}, it can be useful to explore #' the group sizes in \code{db} (and the number of heavy/long chain sequences in those groups). #' #' @references #' \enumerate{ #' \item Smith DS, et al. Di- and trinucleotide target preferences of somatic #' mutagenesis in normal and autoreactive B cells. #' J Immunol. 1996 156:2642-52. #' \item Glanville J, Kuo TC, von Budingen H-C, et al. #' Naive antibody gene-segment frequencies are heritable and unaltered by #' chronic lymphocyte ablation. #' Proc Natl Acad Sci USA. 2011 108(50):20066-71. #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4:358. #' } #' #' @seealso See \link{calcTargetingDistance} for generating nucleotide distance matrices #' from a \link{TargetingModel} object. See \link{HH_S5F}, \link{HH_S1F}, #' \link{MK_RS1NF}, \link[alakazam]{getDNAMatrix}, and \link[alakazam]{getAAMatrix} #' for individual model details. \link[alakazam]{getLocus} to get locus #' values based on allele calls. #' #' @examples #' # Subset example data to one sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, sample_id == "-1h") #' #' # Use genotyped V assignments, Hamming distance, and normalize by junction length #' # First partition based on V and J assignments, then by junction length #' # Take into consideration ambiguous V and J annotations #' dist <- distToNearest(db, sequenceColumn="junction", #' vCallColumn="v_call_genotyped", jCallColumn="j_call", #' model="ham", first=FALSE, VJthenLen=TRUE, normalize="len") #' #' # Plot histogram of non-NA distances #' p1 <- ggplot(data=subset(dist, !is.na(dist_nearest))) + #' theme_bw() + #' ggtitle("Distance to nearest: Hamming") + #' xlab("distance") + #' geom_histogram(aes(x=dist_nearest), binwidth=0.025, #' fill="steelblue", color="white") #' plot(p1) #' #' @export distToNearest <- function(db, sequenceColumn="junction", vCallColumn="v_call", jCallColumn="j_call", model=c("ham", "aa", "hh_s1f", "hh_s5f", "mk_rs1nf", "mk_rs5nf", "m1n_compat", "hs1f_compat"), normalize=c("len", "none"), symmetry=c("avg", "min"), first=TRUE, VJthenLen=TRUE, nproc=1, fields=NULL, cross=NULL, mst=FALSE, subsample=NULL, progress=FALSE, cellIdColumn=NULL, locusColumn="locus", locusValues=c("IGH"), onlyHeavy=TRUE, keepVJLgroup=TRUE) { # Hack for visibility of foreach index variables i <- NULL # Initial checks model <- match.arg(model) normalize <- match.arg(normalize) symmetry <- match.arg(symmetry) if (!is.data.frame(db)) { stop('Must submit a data frame') } # Check base input check <- checkColumns(db, c(sequenceColumn, vCallColumn, jCallColumn, fields, cross)) if (check != TRUE) { stop(check) } # Check locusColumn # message locusColumn is required check <- checkColumns(db, locusColumn) if (check != TRUE) { stop(check, ". `locusColumn`, with default value 'locus', is now a required parameter.") } if (is.null(locusColumn)) { stop("`locusColumn`, is now a required parameter.") } # Check single-cell input if (!is.null(cellIdColumn)) { check <- checkColumns(db, c(cellIdColumn)) if (check != TRUE) { stop(check) } } # Cast all columns to character columns <- c(sequenceColumn, vCallColumn, jCallColumn, fields, cross, cellIdColumn, locusColumn) columns <- columns[!is.null(columns) & columns %in% names(db)] for (cl in columns) { db[[cl]] <- as.character(db[[cl]]) } # Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn)) # Create new column for distance to nearest neighbor db$TMP_DIST_NEAREST <- rep(NA, nrow(db)) db$DTN_ROW_ID <- 1:nrow(db) # Check valid loci if (any(is.na(db[[locusColumn]]))) { stop("The locus column contains NA loci annotations.") } # check locus column contains valid values # We could use the airr schema: valid_loci <- airr::RearrangementSchema['locus'][['enum']] valid_loci <- c("IGH", "IGI", "IGK", "IGL", "TRA", "TRB", "TRD", "TRG") seen_loci <- unique(db[[locusColumn]]) check <- !all(seen_loci %in% valid_loci) if (check) { not_valid <- paste("'",setdiff(seen_loci,valid_loci),"'", sep="",collapse=",") stop("The locus column contains invalid loci annotations: ",not_valid,".") } invalid_locus_values <- locusValues[locusValues %in% valid_loci == F] if (length(invalid_locus_values)>0) { stop("`locusValues` contains invalid loci annotations: ",paste(invalid_locus_values,collapse=", "),".") } # Single-cell mode? if (!is.null(cellIdColumn)) { singleCell <- TRUE } else { singleCell <- FALSE } # Disallow multiple heavy chains per cell # sequences with cell_id==NA are not considered, table's default # is useNA="no" if (singleCell) { # check multiple heavy chains x <- sum(table(db[[cellIdColumn]][db[[locusColumn]] == "IGH"]) > 1) if (x > 0) { stop(paste(x, "cell(s) with multiple heavy chains found. One heavy chain per cell is expected.")) } # check multiple beta chains x <- sum(table(db[[cellIdColumn]][db[[locusColumn]] == "TRB"]) > 1) if (x > 0) { stop(paste(x, "cell(s) with multiple beta chains found. One beta chain per cell is expected.")) } # check multiple delta chains x <- sum(table(db[[cellIdColumn]][db[[locusColumn]] == "TRD"]) > 1) if (x > 0) { stop(paste(x, "cell(s) with multiple delta chains found. One delta chain per cell is expected.")) } } # Check for invalid characters valid_seq <- sapply(db[[sequenceColumn]], allValidChars, getCharsInModel(model)) not_valid_seq <- which(!valid_seq) if (length(not_valid_seq) > 0) { warning("Invalid sequence characters in the ", sequenceColumn, " column. ", length(not_valid_seq), " sequence(s) removed") db <- db[valid_seq, ] } # junction length columns (prep for groupGenes) junc_len <- "JUNC_LEN" db[[junc_len]] <- stri_length(db[[sequenceColumn]]) # fields groups db$DTN_TMP_FIELD <- db %>% group_by(!!!rlang::syms(fields)) %>% group_indices() # create V+J grouping, or V+J+L grouping if (VJthenLen) { # 2-stage partitioning using first V+J and then L # V+J only first # creates $vj_group db <- db %>% ungroup() %>% group_by(!!rlang::sym("DTN_TMP_FIELD")) %>% do(groupGenes(.data, v_call=vCallColumn, j_call=jCallColumn, junc_len=NULL, cell_id=cellIdColumn, locus=locusColumn, only_heavy=onlyHeavy, first=first)) %>% ungroup() # L (later) group_cols <- c("vj_group", junc_len) } else { # 1-stage partitioning using V+J+L simultaneously # creates $vj_group # note that despite the name (VJ), this is based on V+J+L db <- db %>% ungroup() %>% group_by(!!rlang::sym("DTN_TMP_FIELD")) %>% do(groupGenes(.data, v_call=vCallColumn, j_call=jCallColumn, junc_len=junc_len, cell_id=cellIdColumn, locus=locusColumn, only_heavy=onlyHeavy, first=first)) %>% ungroup() group_cols <- c("vj_group") } # groups to use if (!is.null(fields)) { group_cols <- append(group_cols,fields) # make vj_group unique across fields by pasting field group db <- db %>% dplyr::rowwise() %>% mutate(vj_group=paste("F",!!rlang::sym("DTN_TMP_FIELD"),"_",!!rlang::sym("vj_group"), sep="", collapse = "")) %>% ungroup() } db[['DTN_TMP_FIELD']] <- NULL # unique groups # not necessary but good practice to force as df and assign colnames # (in case group_cols has length 1; which can happen in groupBaseline) uniqueGroups <- data.frame(unique(db[, group_cols]), stringsAsFactors=FALSE) colnames(uniqueGroups) <- group_cols rownames(uniqueGroups) <- NULL # indices # crucial to have simplify=FALSE # (otherwise won't return a list if uniqueClones has length 1) uniqueGroupsIdx <- sapply(1:nrow(uniqueGroups), function(i){ curGroup <- data.frame(uniqueGroups[i, ], stringsAsFactors=FALSE) colnames(curGroup) <- group_cols # match for each field curIdx <- sapply(group_cols, function(coln){ db[[coln]]==curGroup[, coln] }, simplify=FALSE) curIdx <- do.call(rbind, curIdx) # intersect to get match across fields curIdx <- which(colSums(curIdx)==length(group_cols)) # sanity check # no NA stopifnot( all(!is.na(curIdx)) ) # index within range of db stopifnot( max(curIdx) <= nrow(db) ) return(curIdx) }, simplify=FALSE) # Create cluster of nproc size and export namespaces # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if( nproc==1 ) { # If needed to run on a single core/cpu then, register DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } else if( nproc > 1 ) { cluster <- parallel::makeCluster(nproc, type="PSOCK") registerDoParallel(cluster,cores=nproc) } else { stop('Nproc must be positive.') } # Export groups to the clusters if (nproc > 1) { # This subsetting improves performance required_cols <- unique(c(group_cols,cross,locusColumn,sequenceColumn,"TMP_DIST_NEAREST")) db_notused_cols <- db %>% select(c(!!rlang::sym("DTN_ROW_ID"), !any_of(required_cols))) db <- db %>% select(c(!!rlang::sym("DTN_ROW_ID"), any_of(required_cols))) export_functions <- list("db", "uniqueGroupsIdx", "cross", "mst", "subsample", "sequenceColumn", "model", "normalize", "symmetry", "nearestDist", "HH_S1F_Distance", "MK_RS1NF_Distance", "HH_S5F_Distance", "MK_RS5NF_Distance", "HS1F_Compat", "M1N_Compat", "calcTargetingDistance", "findUniqSeq", "pairwise5MerDist", "nonsquare5MerDist", "singleCell", "locusColumn", "locusValues") parallel::clusterExport(cluster, export_functions, envir=environment()) } n_groups <- length(uniqueGroupsIdx) if (progress) { pb <- progressBar(n_groups) } tryCatch(list_db <- foreach(i=1:n_groups, .errorhandling='stop') %dopar% { # wrt db idx <- uniqueGroupsIdx[[i]] # if (singleCell) { # # only use IGH, TRB, TRD # # wrt idx # idxBool <- db[[locusColumn]][idx] %in% c("IGH", "TRB", "TRD") # } else { # idxBool <- rep(TRUE, length(idx)) # } # for the distance calculation use only # sequences with locus values specified in `locusValues` idxBool <- toupper(db[[locusColumn]][idx]) %in% locusValues db_group <- db[idx, ] crossGroups <- NULL if (!is.null(cross)) { x <- dplyr::group_by(db_group, !!!rlang::syms(cross)) crossGroups <- dplyr::group_indices(x) } arrSeqs <- db[[sequenceColumn]][idx] db_group$TMP_DIST_NEAREST[idxBool] <- nearestDist(arrSeqs[idxBool], model=model, normalize=normalize, symmetry=symmetry, crossGroups=crossGroups[idxBool], mst=mst, subsample=subsample) # Update progress if (progress) { pb$tick() } return(db_group) }, error = function(e) { if (nproc > 1 & grepl("Error in unserialize(socklist[[n]]) : error reading from connection", e, fixed=TRUE)) { warning("There is an error running the code in parallel. Try with nproc=1.") } stop(e) } ) # Convert list from foreach into a db data.frame db <- do.call(rbind, list_db) # Stop the cluster and add back not used colums if (nproc > 1) { parallel::stopCluster(cluster) db <- db %>% left_join(db_notused_cols, by= "DTN_ROW_ID") } db <- db[order(db$DTN_ROW_ID), ] if (!is.null(cross)) { db$cross_dist_nearest <- db$TMP_DIST_NEAREST } else { db$dist_nearest <- db$TMP_DIST_NEAREST } # prepare db for return if ((!VJthenLen) && keepVJLgroup) { db$vjl_group <- db[["vj_group"]] } db <- db[, !(names(db) %in% c(junc_len, "vj_group", "DTN_ROW_ID", "V1", "J1","TMP_DIST_NEAREST"))] return(db) } #### Distance Threshold Detection #### #' Find distance threshold #' #' \code{findThreshold} automatically determines an optimal threshold for clonal assignment of #' Ig sequences using a vector of nearest neighbor distances. It provides two alternative methods #' using either a Gamma/Gaussian Mixture Model fit (\code{method="gmm"}) or kernel density #' fit (\code{method="density"}). #' #' @param distances numeric vector containing nearest neighbor distances. #' @param method string defining the method to use for determining the optimal threshold. #' One of \code{"gmm"} or \code{"density"}. See Details for methodological #' descriptions. #' @param edge upper range as a fraction of the data density to rule initialization of #' Gaussian fit parameters. Default value is 90% of the entries (0.9). #' Applies only when \code{method="density"}. . #' @param cross supplementary nearest neighbor distance vector output from \link{distToNearest} #' for initialization of the Gaussian fit parameters. #' Applies only when \code{method="gmm"}. #' @param subsample maximum number of distances to subsample to before threshold detection. #' @param model allows the user to choose among four possible combinations of fitting curves: #' \code{"norm-norm"}, \code{"norm-gamma"}, \code{"gamma-norm"}, #' and \code{"gamma-gamma"}. Applies only when \code{method="gmm"}. #' @param cutoff method to use for threshold selection: the optimal threshold \code{"opt"}, #' the intersection point of the two fitted curves \code{"intersect"}, or #' a value defined by user for one of the sensitivity or specificity \code{"user"}. #' Applies only when \code{method="gmm"}. #' @param sen sensitivity required. Applies only when \code{method="gmm"} and \code{cutoff="user"}. #' @param spc specificity required. Applies only when \code{method="gmm"} and \code{cutoff="user"}. #' #' @param progress if \code{TRUE} print a progress bar. #' @return #' \itemize{ #' \item \code{"gmm"} method: Returns a \link{GmmThreshold} object including the #' \code{threshold} and the function fit parameters, i.e. #' mixing weight, mean, and standard deviation of a Normal distribution, or #' mixing weight, shape and scale of a Gamma distribution. #' \item \code{"density"} method: Returns a \link{DensityThreshold} object including the optimum #' \code{threshold} and the density fit parameters. #' } #' #' @details #' \itemize{ #' \item \code{"gmm"}: Performs a maximum-likelihood fitting procedure, for learning #' the parameters of two mixture univariate, either Gamma or Gaussian, distributions #' which fit the bimodal distribution entries. Retrieving the fit parameters, #' it then calculates the optimum threshold \code{method="optimal"}, where the #' average of the sensitivity plus specificity reaches its maximum. In addition, #' the \code{findThreshold} function is also able #' to calculate the intersection point (\code{method="intersect"}) of the two fitted curves #' and allows the user to invoke its value as the cut-off point, instead of optimal point. #' \item \code{"density"}: Fits a binned approximation to the ordinary kernel density estimate #' to the nearest neighbor distances after determining the optimal #' bandwidth for the density estimate via least-squares cross-validation of #' the 4th derivative of the kernel density estimator. The optimal threshold #' is set as the minimum value in the valley in the density estimate #' between the two modes of the distribution. #' } #' #' @seealso See \link{distToNearest} for generating the nearest neighbor distance vectors. #' See \link{plotGmmThreshold} and \link{plotDensityThreshold} for plotting output. #' #' @note #' Visually inspecting the resulting distribution fits is strongly recommended when using #' either fitting method. Empirical observations imply that the bimodality #' of the distance-to-nearest distribution is detectable for a minimum of 1,000 distances. #' Larger numbers of distances will improve the fitting procedure, although this can come #' at the expense of higher computational demands. #' #' @examples #' \donttest{ #' # Subset example data to 50 sequences, one sample and isotype as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, sample_id == "-1h" & c_call=="IGHG")[1:50,] #' #' # Use nucleotide Hamming distance and normalize by junction length #' db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call", #' jCallColumn="j_call", model="ham", normalize="len", nproc=1) #' #' # Find threshold using the "gmm" method with user defined specificity #' output <- findThreshold(db$dist_nearest, method="gmm", model="gamma-gamma", #' cutoff="user", spc=0.99) #' plot(output, binwidth=0.02, title=paste0(output@model, " loglk=", output@loglk)) #' print(output) #' } #' @export findThreshold <- function (distances, method=c("density", "gmm"), edge=0.9, cross=NULL, subsample=NULL, model=c("gamma-gamma", "gamma-norm", "norm-gamma", "norm-norm"), cutoff=c("optimal", "intersect", "user"), sen=NULL, spc=NULL, progress=FALSE){ # Check arguments method <- match.arg(method) model <- match.arg(model) cutoff <- match.arg(cutoff) # Subsample input distances if(!is.null(subsample)) { subsample <- min(length(distances), subsample) distances <- sample(distances, subsample, replace=FALSE) } if (method == "gmm") { if (cutoff == "user"){ if (is.null(sen) & is.null(spc)) { cat("Error: one of 'sen' or 'spc' values should be specified.") output <- NA } else if (!is.null(sen) & !is.null(spc)) { cat("Error: only one of 'sen' or 'spc' values can be specified.") output <- NA } else { output <- gmmFit(ent=distances, edge=edge, cross=cross, model=model, cutoff=cutoff, sen=sen, spc=spc, progress=progress) } } else { output <- gmmFit(ent=distances, edge=edge, cross=cross, model=model, cutoff=cutoff, sen=sen, spc=spc, progress=progress) } } else if (method == "density") { output <- smoothValley(distances) } else { cat("Error: assigned method has not been found.\n") output <- NA } return(output) } # Find distance threshold with \code{"density"} Method # # Infer value of the minimum between the two modes in a bimodal distribution. # # @param distances numeric vector of distances. # # @return Returns distance threshold that separates two modes of the input distribution. # # @details # The distance to nearest neighbor can be used to estimate a threshold for assigning Ig # sequences to clonal groups. A histogram of the resulting vector is often bimodal, # with the ideal threshold being a value that separates the two modes. This function takes # as input a vector of such distances and infers the ideal threshold. # # @seealso # \itemize{ # \item See \link{distToNearest} for details on generating the input distance vector. # \item See \link{gmmFit} for a different threshold inference methodology. # \item See \link{findThreshold} to switch between available methods. #} # # # @examples # # Subset example data to one sample as a demo # data(ExampleDb, package="alakazam") # db <- subset(ExampleDb, sample_id == "-1h") # # # Use genotyped V assignments, HS1F model, and normalize by junction length # dist_hs1f <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", # jCallColumn="j_call", # model="hs1f", first=FALSE, normalize="len") # # # using findThreshold switch # threshold <- findThreshold(dist_hs1f$dist_nearest, method="density") # # or # threshold <- smoothValley(dist_hs1f$dist_nearest) # # # Plot histogram of non-NA distances # p1 <- ggplot(data=subset(dist_hs1f, !is.na(dist_nearest))) + theme_bw() + # ggtitle("Distance to nearest: hs1f") + xlab("distance") + # geom_histogram(aes(x=dist_nearest), binwidth=0.025, # fill="steelblue", color="white") + # geom_vline(xintercept=threshold, linetype="dashed") # plot(p1) # # @export smoothValley <- function(distances) { # Remove NA, NaN, and infinite distances distances <- distances[!is.na(distances) & !is.nan(distances) & !is.infinite(distances)] unique_distances <- unique(distances) # Gaussian distribution bandwidth scale parameter # gaussian_scaling <- (1/(4 * pi))^(1/10) # Ideal bandwidth if (length(unique_distances) < 3 ) { stop("The `smoothValley` funcion used by the density method requires ", "at least 3 unique distance values.\n", "Found distance values: ", paste(unique_distances, collapse=", ")) } bandwidth <- h.ucv(unique_distances, 4)$h #bandwidth <- kedd::h.ucv(distances, 4)$h #bandwidth <- ks::hucv(unique(distances), deriv.order=4) # Density estimate dens <- KernSmooth::bkde(distances, bandwidth=bandwidth, canonical=TRUE) #dens <- KernSmooth::bkde(distances, bandwidth=bandwidth) xdens <- dens$x ydens <- dens$y #dens <- ks::kde(distances, h=bandwidth*gaussian_scaling, binned=TRUE) #xdens <- dens$eval.points #ydens <- dens$estimate # Find threshold tryCatch(threshold <- xdens[which(diff(sign(diff(ydens))) == 2)[1] + 1], error = function(e) { warning('No minimum was found between two modes.') return(NULL) }) results <- new("DensityThreshold", x=distances, bandwidth=bandwidth, xdens=xdens, ydens=ydens, threshold=threshold) return(results) } # Find distance threshold with Gaussian Mixture Method # # Fits a bimodal distribution with two Gaussian functions and calculates maximum of the average of the # Sensitivity plus Specificity corresponding to the Gaussian distributions. # # @param ent numeric vector of distances returned from \link{distToNearest} function. # @param edge upper range (a fraction of the data density) to rule initialization of # Gaussian fit parameters. Default value is equal to \eqn{90}\% of the entries. # @param cross a supplementary info (numeric vector) invoked from \link{distToNearest} # function, to support initialization of the Gaussian fit parameters. # @param progress if \code{TRUE} print progress. # # @return returns an object including optimum "\code{threshold}" cut and the Gaussian fit parameters, # such as mixing proportion ("\code{omega1}" and "\code{omega2}"), mean ("\code{mu1}" and "\code{mu2}"), # and standard deviation ("\code{sigma1}" and "\code{sigma2}"). Returns "\code{NULL}" if no fit has found. # # @seealso # \itemize{ # \item See \link{distToNearest} for details on generating the input distance vector. # \item See \link{smoothValley} for a different threshold inference methodology. # \item See \link{findThreshold} to switch between available methods. #} # # # @details This function follows a Gaussian Mixture Model (GMM) procedure, # including the Expectation Maximization (EM) algorithm, for learning the parameters # of two univariate Gaussians which fit the bimodal distribution entries. # Retrieving the fit parameters, it then calculates, analytically, the optimum threshold, # where the average of the Sensitivity plus Specificity reaches its maximum. This threshold # can be then invoked for assigning Ig sequences to clonal groups. # # @examples # # Subset example data to one sample as a demo # data(ExampleDb, package="alakazam") # db <- subset(ExampleDb, sample_id == "-1h") # # # Use nucleotide Hamming distance and normalize by junction length # db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", # jCallColumn="j_call", model="ham", first=FALSE, normalize="len", nproc=1) # # # To find the Threshold cut use either findThreshold-switch # output <- findThreshold(db$dist_nearest, method="gmm", edge=0.9) # # or # output <- gmmFit(db$dist_nearest, edge=0.9) gmmFit <- function(ent, edge=0.9, cross=NULL, model, cutoff, sen, spc, progress=FALSE) { #************* Filter Unknown Data *************# ent <- ent[!is.na(ent) & !is.nan(ent) & !is.infinite(ent)] if (is.null(cross)) { m <- FALSE } else { m <- mean(cross, na.rm = TRUE) } #************* Defult edge *************# cut <- edge*length(ent) #************* Define Scan Step For Initializing *************# if (ent[which.max(ent)] <= 5) { scan_step <- 0.1 } else { scan_step <- 1 } #************* Print some info *************# if (progress) { valley_loc <- 0 while (1) { valley_loc <- valley_loc + scan_step if ( length(ent[ent<=valley_loc]) > cut ) break } n_iter <- ceiling(valley_loc/scan_step)-1 cat(" STEP> ", "Parameter initialization\n", sep="") cat(" VALUES> ", length(ent), "\n", sep="") cat("ITERATIONS> ", n_iter, "\n", sep="") pb <- progressBar(n_iter) } #************* set rand seed *************# set.seed(NULL) #************* define Number of Gaussians *************# num_G <- 2 vec.omega1 <- 0; vec.omega2 <- 0 vec.mu1 <- 0; vec.mu2 <- 0 vec.sigma1 <- 0; vec.sigma2 <- 0 vec.lkhood <- 0 valley.itr <- 0 valley_loc <- 0 nEve <- length(ent) while (1) { #************* guess the valley loc *************# valley_loc <- valley_loc + scan_step if ( length(ent[ent<=valley_loc]) > cut ) break #************* Choosing Random Omega *************# omega <- runif(1) omega <- c(omega, 1.-omega) #************* Choosing Random Mean *************# mu_int <- mean(ent[ent<=valley_loc]) mu_int <- c(mu_int, mean(ent[ent>valley_loc])) #************* Choosing Random Sigma *************# sigma_int <- sd(ent[entvalley_loc])) #************* EM Algorithm *************# temp_lk <- 0 itr <- 0 while (1){ mu <- 0 sigma <- 0 for (j in 1:num_G){ mu[j] <- mu_int[j] sigma[j] <- sigma_int[j] } #************* E-step Expectation *************# resp <- array(0, dim=c(nEve,num_G)) for(i in 1:nEve){ for (j in 1:num_G) resp[i,j] <- omega[j]*dnorm(ent[i], mu[j], sigma[j]) resp[i,] <- resp[i,]/sum(resp[i,]) } #************* M-step Maximization *************# for (j in 1:num_G){ m_c <- sum(resp[,j]) omega[j] <- m_c / nEve mu[j] <- sum(resp[,j]*ent) mu[j] <- mu[j] / m_c sigma[j] <- sum(resp[,j]*(ent-mu[j])*(ent-mu[j])) sigma[j] <- sigma[j] / m_c sigma[j] <- sqrt(sigma[j]) } #************* Log-likelihood calculation *************# log_lk <- 0. for (i in 1:nEve){ s <- 0 for (j in 1:num_G) s <- s + omega[j]*dnorm(ent[i], mu[j], sigma[j]) log_lk <- log_lk + log(s, base = exp(1)) } log_lk_err <- abs(log_lk - temp_lk) itr = itr + 1 #print(paste0("scaned: ", valley_loc, " itr # ", itr, " -> ", log_lk_err)) if (is.na(log_lk_err) | is.nan(log_lk_err) | is.infinite(log_lk_err)) break if (log_lk_err < 1.e-7) break temp_lk <- log_lk; } #************************************************************# #************* JUST FOR VISUALIZATION PURPOSES *************# # print(paste0("scaned: ", valley_loc, " --------> Log-Likelihood: ", log_lk)) # if (ent[which.min(ent)] >= 0 & ent[which.max(ent)] <= 5) { # h_min <- 0.0 # h_max <- 1 # dh = 0.02 # } else { # h_min <- 0.0 # h_max <- ent[which.max(ent)] # dh = 1 # } # h <- hist(ent, plot = FALSE, breaks=seq(h_min, h_max, by=dh)) # plot(h, freq=FALSE, col="steelblue", border="white", xlim=c(h_min, h_max)) # curve(omega[1]*dnorm(x, mu[1], sigma[1]), add=TRUE, col="darkblue", lwd=2, xlim = c(h_min, h_max)) # curve(omega[2]*dnorm(x, mu[2], sigma[2]), add=TRUE, col="darkred", lwd=2, xlim = c(h_min, h_max)) #************************************************************# #************************************************************# if (!is.na(log_lk_err) & !is.nan(log_lk_err) & !is.infinite(log_lk_err)){ if (!as.logical(m)){ valley.itr <- valley.itr + 1 vec.omega1[valley.itr] <- omega[1] vec.omega2[valley.itr] <- omega[2] vec.mu1[valley.itr] <- mu[1] vec.mu2[valley.itr] <- mu[2] vec.sigma1[valley.itr] <- sigma[1] vec.sigma2[valley.itr] <- sigma[2] vec.lkhood[valley.itr] <- log_lk } else if ((mu[1]< m & m < mu[2]) | (mu[2]< m & m < mu[1]) | (mu[1]< m & mu[2]< m) ){ valley.itr <- valley.itr + 1 vec.omega1[valley.itr] <- omega[1] vec.omega2[valley.itr] <- omega[2] vec.mu1[valley.itr] <- mu[1] vec.mu2[valley.itr] <- mu[2] vec.sigma1[valley.itr] <- sigma[1] vec.sigma2[valley.itr] <- sigma[2] vec.lkhood[valley.itr] <- log_lk } } # Update progress if (progress) { pb$tick() } } if (valley.itr != 0) { # MaxLoc <- which.max(vec.lkhood) MaxLoc <- which.max(abs(vec.lkhood)) omega[1] <- vec.omega1[MaxLoc]; omega[2] <- vec.omega2[MaxLoc] mu[1] <- vec.mu1[MaxLoc]; mu[2] <- vec.mu2[MaxLoc] sigma[1] <- vec.sigma1[MaxLoc]; sigma[2] <- vec.sigma2[MaxLoc] # Invoke Gaussians parameters omega.gmm <- c(omega[1], omega[2]) mu.gmm <- c(mu[1], mu[2]) sigma.gmm <- c(sigma[1], sigma[2]) fit_results <- rocSpace(ent=ent, omega.gmm=omega.gmm , mu.gmm=mu.gmm, sigma.gmm=sigma.gmm, model=model, cutoff=cutoff, sen=sen, spc=spc, progress=progress) results <- new("GmmThreshold", x=ent, model=model, cutoff=cutoff, a1=fit_results@a1, b1=fit_results@b1, c1=fit_results@c1, a2=fit_results@a2, b2=fit_results@b2, c2=fit_results@c2, loglk=fit_results@loglk, threshold=fit_results@threshold, sensitivity=fit_results@sensitivity, specificity=fit_results@specificity, pvalue=fit_results@pvalue) } else { print("Error: No fit found") results <- NULL } return(results) } rocSpace <- function(ent, omega.gmm, mu.gmm, sigma.gmm, model, cutoff, sen, spc, progress=FALSE) { func <- model bits <- strsplit(func,'-')[[1]] # Define mixture Function properties if (bits[1] == "norm"){ func1.0 <- round(omega.gmm[1], digits = 3) # -> prob: omega func1.1 <- mu.gmm[1] # -> mean: mu func1.2 <- sigma.gmm[1] # -> sd: sigma } else if (bits[1] == "gamma"){ func1.0 <- round(omega.gmm[1], digits = 3) # -> prob: omega func1.1 <- (mu.gmm[1]/sigma.gmm[1])*(mu.gmm[1]/sigma.gmm[1]) # -> shape: k func1.2 <- sigma.gmm[1]*sigma.gmm[1]/mu.gmm[1] # -> scale: theta } if (bits[2] == "norm"){ func2.1 = mu.gmm[2] # -> mean: mu func2.2 = sigma.gmm[2] # -> sd: sigma } else if (bits[2] == "gamma"){ func2.1 <- (mu.gmm[2]/sigma.gmm[2])*(mu.gmm[2]/sigma.gmm[2]) # -> shape: k func2.2 <- sigma.gmm[2]*sigma.gmm[2]/mu.gmm[2] # -> scale: theta } # Save mixture Function properties gmmfunc1.1 <- func1.1 gmmfunc1.2 <- func1.2 gmmfunc2.1 <- func2.1 gmmfunc2.2 <- func2.2 set.seed(NULL) # options(warn=-1) LOG_LIK<-0 if (progress) { cat(" STEP> ", "Fitting ", func, "\n", sep="") pb <- progressBar(15) } for (i in 1:15) { #itr<-1 key<-FALSE while (!key){ # print(paste0(i,":",itr)) # Fit mixture Functions MixModel <- try(suppressWarnings(fitdistr(na.exclude(ent), mixFunction, first_curve = bits[1], second_curve = bits[2], start=list(omega = func1.0, func1.1 = func1.1, func1.2 = func1.2, func2.1 = func2.1, func2.2 = func2.2), lower = c(0.001, 0.001, 0.001, 0.001, 0.001), upper = c(0.999, +Inf, +Inf, +Inf, +Inf))), silent = TRUE) if (inherits(MixModel, "try-error")) { func1.0 <- runif(1) func1.1 <- abs(gmmfunc1.1 + sample(c(-1,1), 1)*runif(1)) func1.2 <- abs(gmmfunc1.2 + sample(c(-1,1), 1)*runif(1)) func2.1 <- abs(gmmfunc2.1 + sample(c(-1,1), 1)*runif(1)) func2.2 <- abs(gmmfunc2.2 + sample(c(-1,1), 1)*runif(1)) #itr<-itr+1 next } else if ( (bits[1] == "norm" & bits[2] == "gamma" & MixModel$estimate[[2]] > MixModel$estimate[[4]] * MixModel$estimate[[5]]) | (bits[1] == "gamma" & bits[2] == "norm" & MixModel$estimate[[2]] * MixModel$estimate[[3]] > MixModel$estimate[[4]]) | MixModel$estimate[[1]] == 0.001 | MixModel$estimate[[1]] == 0.999) { func1.0 <- runif(1) func1.1 <- abs(gmmfunc1.1 + sample(c(-1,1), 1)*runif(1)) func1.2 <- abs(gmmfunc1.2 + sample(c(-1,1), 1)*runif(1)) func2.1 <- abs(gmmfunc2.1 + sample(c(-1,1), 1)*runif(1)) func2.2 <- abs(gmmfunc2.2 + sample(c(-1,1), 1)*runif(1)) # print("here") #itr<-itr+1 next } else { key<-TRUE } } # print(paste0(func, " fit done. Loglik= ", round(MixModel$loglik, digits = 2))) # Invoke fit parameters # log_lik <- round(MixModel$loglik, digits = 2) log_lik <- round(abs(MixModel$loglik), digits = 2) if (log_lik > LOG_LIK){ LOG_LIK <- log_lik FUNC1.0 <- MixModel$estimate[[1]] FUNC1.1 <- MixModel$estimate[[2]] FUNC1.2 <- MixModel$estimate[[3]] FUNC2.0 <- 1. - MixModel$estimate[[1]] FUNC2.1 <- MixModel$estimate[[4]] FUNC2.2 <- MixModel$estimate[[5]] } # New fit parameters for next loop func1.0 <- runif(1) func1.1 <- abs(gmmfunc1.1 + sample(c(-1,1), 1)*runif(1)) func1.2 <- abs(gmmfunc1.2 + sample(c(-1,1), 1)*runif(1)) func2.1 <- abs(gmmfunc2.1 + sample(c(-1,1), 1)*runif(1)) func2.2 <- abs(gmmfunc2.2 + sample(c(-1,1), 1)*runif(1)) # if (i==1 & itr == 1) break if (progress) { pb$tick() } } # options(warn=0) # Invoke best fit parameters log_lik <- LOG_LIK func1.0 <- FUNC1.0 func1.1 <- FUNC1.1 func1.2 <- FUNC1.2 func2.0 <- FUNC2.0 func2.1 <- FUNC2.1 func2.2 <- FUNC2.2 # order fit parameters if (bits[1]=="norm" & bits[2]=="norm" & func1.1>func2.1) { FUNC0 <- func1.0 FUNC1 <- func1.1 FUNC2 <- func1.2 func1.0 <- func2.0 func1.1 <- func2.1 func1.2 <- func2.2 func2.0 <- FUNC0 func2.1 <- FUNC1 func2.2 <- FUNC2 } else if (bits[1]=="gamma" & bits[2]=="gamma" & func1.1*func1.2>func2.1*func2.2) { FUNC0 <- func1.0 FUNC1 <- func1.1 FUNC2 <- func1.2 func1.0 <- func2.0 func1.1 <- func2.1 func1.2 <- func2.2 func2.0 <- FUNC0 func2.1 <- FUNC1 func2.2 <- FUNC2 } # domain [t1,t2] under distribution t1<-min(ent) t2<-max(ent) # domain [minInt,maxInt] to search for opt and root if (bits[1] == "norm") { minInt<-func1.1 } else if (bits[1] == "gamma") { minInt<-func1.1*func1.2 } if (bits[2] == "norm") { maxInt<-func2.1 } else if (bits[2] == "gamma") { maxInt<-func2.1*func2.2 } if (cutoff == "optimal"){ # Calculate optimum opt <- optimize(avgSenSpc, interval = c(minInt, maxInt), tol=1e-8, maximum = TRUE, t1=t1, t2=t2, first_curve = bits[1], second_curve = bits[2], func1.0=func1.0, func1.1=func1.1, func1.2=func1.2, func2.0=func2.0, func2.1=func2.1, func2.2=func2.2) threshold <- opt$maximum } else if (cutoff == "intersect") { # Calculate intersection intxn <- uniroot(intersectPoint, interval = c(minInt, maxInt), tol=1e-8, extendInt="yes", first_curve = bits[1], second_curve = bits[2], func1.0=func1.0, func1.1=func1.1, func1.2=func1.2, func2.0=func2.0, func2.1=func2.1, func2.2=func2.2) threshold <- intxn$root } else if (cutoff == "user") { user <- uniroot(userDefineSenSpc, interval = c(t1, t2), tol=1e-8, extendInt="no", t1=t1, t2=t2, first_curve = bits[1], second_curve = bits[2], sen = sen, spc = spc, func1.0=func1.0, func1.1=func1.1, func1.2=func1.2, func2.0=func2.0, func2.1=func2.1, func2.2=func2.2) threshold <- user$root } # Calculate Sensitivity and Specificity if (bits[1]=="norm") { TP = normArea(t1=t1, t2=threshold, omega=func1.0, mu=func1.1, sigma=func1.2) } else if (bits[1]=="gamma") { TP = gammaArea(t1=t1, t2=threshold, omega=func1.0, k=func1.1, theta=func1.2) } if (bits[1]=="norm") { FN = normArea(t1=threshold, t2=t2, omega=func1.0, mu=func1.1, sigma=func1.2) } else if (bits[1]=="gamma") { FN = gammaArea(t1=threshold, t2=t2, omega=func1.0, k=func1.1, theta=func1.2) } if (bits[2]=="norm") { TN = normArea(t1=threshold, t2=t2, omega=func2.0, mu=func2.1, sigma=func2.2) } else if (bits[2]=="gamma") { TN = gammaArea(t1=threshold, t2=t2, omega=func2.0, k=func2.1, theta=func2.2) } if (bits[2]=="norm") { FP = normArea(t1=t1, t2=threshold, omega=func2.0, mu=func2.1, sigma=func2.2) } else if (bits[2]=="gamma") { FP = gammaArea(t1=t1, t2=threshold, omega=func2.0, k=func2.1, theta=func2.2) } sensitivity <- TP/(TP+FN) specificity <- TN/(TN+FP) # Hartigans dip statistic (HDS) test invisible(capture.output(pvalue <- dip.test(ent)$p.value[[1]], type="message")) fit_results <- new("GmmThreshold", x=numeric(), model=character(), cutoff=character(), a1=func1.0, b1=func1.1, c1=func1.2, a2=func2.0, b2=func2.1, c2=func2.2, loglk=log_lik, threshold=threshold, sensitivity=sensitivity, specificity=specificity, pvalue=pvalue) return(fit_results) } # Calculates the area (integral) bounded # in domain[t1,t2] under Gamma distribution gammaArea <- function (t1, t2, omega, k, theta){ trm1 <- pgamma(t1/theta, shape=k, lower.tail=FALSE) * gamma(k) trm2 <- pgamma(t2/theta, shape=k, lower.tail=FALSE) * gamma(k) area <- omega*(trm1 - trm2)/gamma(k) return(area) } # Calculates the area (integral) bounded # in domain[t1,t2] under Normal distribution normArea <- function (t1, t2, omega, mu, sigma){ erf1 <- (t1-mu)/(sqrt(2)*sigma) erf1 <- 2*pnorm(erf1*sqrt(2)) - 1 erf2 <- (t2-mu)/(sqrt(2)*sigma) erf2 <- 2*pnorm(erf2*sqrt(2)) - 1 area <- sigma * omega * (-erf1 + erf2) / (2*sigma) return(area) } # find the optimum threshold using # optimize function fit avgSenSpc <- function(t, t1=0, t2=0, first_curve=NULL, second_curve=NULL, func1.0 = 0, func1.1 = 0, func1.2 = 0, func2.0 = 0, func2.1 = 0, func2.2 = 0) { if (first_curve == "norm") { TP <- normArea(t1=t1, t2=t, omega=func1.0, mu=func1.1, sigma=func1.2) FN <- normArea(t1=t, t2=t2, omega=func1.0, mu=func1.1, sigma=func1.2) } else if (first_curve == "gamma") { TP <- gammaArea(t1=t1, t2=t, omega=func1.0, k=func1.1, theta=func1.2) FN <- gammaArea(t1=t, t2=t2, omega=func1.0, k=func1.1, theta=func1.2) } if (second_curve == "norm") { FP <- normArea(t1=t1, t2=t, omega=func2.0, mu=func2.1, sigma=func2.2) TN <- normArea(t1=t, t2=t2, omega=func2.0, mu=func2.1, sigma=func2.2) } else if (second_curve == "gamma") { FP <- gammaArea(t1=t1, t2=t, omega=func2.0, k=func2.1, theta=func2.2) TN <- gammaArea(t1=t, t2=t2, omega=func2.0, k=func2.1, theta=func2.2) } SEN <- TP/(TP + FN) SPC <- TN/(TN + FP) return((SEN + SPC)/2) } # Intersection Function intersectPoint <- function(t, first_curve=NULL, second_curve=NULL, func1.0 = 0, func1.1 = 0, func1.2 = 0, func2.0 = 0, func2.1 = 0, func2.2 = 0) { if (first_curve == "norm") { fit1 <- func1.0*dnorm(t, mean = func1.1, sd = func1.2) } else if (first_curve == "gamma") { fit1 <- func1.0*dgamma(t, shape = func1.1, scale = func1.2) } if (second_curve == "norm") { fit2 <- func2.0*dnorm(t, mean = func2.1, sd = func2.2) } else if (second_curve == "gamma") { fit2 <- func2.0*dgamma(t, shape = func2.1, scale = func2.2) } return(fit1 - fit2) } # useDefineSenSpc userDefineSenSpc <- function(t, t1=0, t2=0, first_curve=NULL, second_curve=NULL, sen=NULL, spc=NULL, func1.0=0, func1.1=0, func1.2=0, func2.0=0, func2.1=0, func2.2=0) { if (!is.null(sen)) { if (first_curve == "norm") { TP <- normArea(t1=t1, t2=t, omega=func1.0, mu=func1.1, sigma=func1.2) FN <- normArea(t1=t, t2=t2, omega=func1.0, mu=func1.1, sigma=func1.2) } else if (first_curve == "gamma") { TP <- gammaArea(t1=t1, t2=t, omega=func1.0, k=func1.1, theta=func1.2) FN <- gammaArea(t1=t, t2=t2, omega=func1.0, k=func1.1, theta=func1.2) } threshold <- (TP/(TP+FN)) - sen } else if (!is.null(spc)) { if (second_curve == "norm") { FP <- normArea(t1=t1, t2=t, omega=func2.0, mu=func2.1, sigma=func2.2) TN <- normArea(t1=t, t2=t2, omega=func2.0, mu=func2.1, sigma=func2.2) } else if (second_curve == "gamma") { FP <- gammaArea(t1=t1, t2=t, omega=func2.0, k=func2.1, theta=func2.2) TN <- gammaArea(t1=t, t2=t2, omega=func2.0, k=func2.1, theta=func2.2) } threshold <- (TN/(TN+FP)) - spc } return(threshold) } # Mixture Functions mixFunction <- function(t, first_curve=NULL, second_curve=NULL, omega = 0, func1.1 = 0, func1.2 = 0, func2.1 = 0, func2.2 = 0) { if (first_curve == "norm"){ r <- omega*dnorm(t, mean=func1.1, sd=func1.2) } else if (first_curve == "gamma") { r <- omega*dgamma(t, shape=func1.1, scale=func1.2) } if (second_curve == "norm"){ r <- r + (1-omega)*dnorm(t, mean=func2.1, sd=func2.2) } else if (second_curve == "gamma") { r <- r + (1-omega)*dgamma(t, shape=func2.1, scale=func2.2) } } #' Plot findThreshold results for the gmm method #' #' \code{plotGmmThreshold} plots the results from \code{"gmm"} method of #' \link{findThreshold}, including the Gaussian distributions, input nearest neighbor #' distance histogram, and threshold selected. #' #' @param data \link{GmmThreshold} object output by the \code{"gmm"} method #' of \link{findThreshold}. #' @param cross numeric vector of distances from \link{distToNearest} to draw as a #' histogram below the \code{data} histogram for comparison purposes. #' @param xmin minimum limit for plotting the x-axis. If \code{NULL} the limit will #' be set automatically. #' @param xmax maximum limit for plotting the x-axis. If \code{NULL} the limit will #' be set automatically. #' @param breaks number of breaks to show on the x-axis. If \code{NULL} the breaks will #' be set automatically. #' @param binwidth binwidth for the histogram. If \code{NULL} the binwidth #' will be set automatically. #' @param title string defining the plot title. #' @param size numeric value for lines in the plot. #' @param silent if \code{TRUE} do not draw the plot and just return the ggplot2 #' object; if \code{FALSE} draw the plot. #' @param ... additional arguments to pass to ggplot2::theme. #' #' @return A ggplot object defining the plot. #' #' @seealso See \link{GmmThreshold} for the the input object definition and #' \link{findThreshold} for generating the input object. See #' \link{distToNearest} calculating nearest neighbor distances. #' #' @examples #' \donttest{ #' # Subset example data to one sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, sample_id == "-1h") #' #' # Use nucleotide Hamming distance and normalize by junction length #' db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", #' jCallColumn="j_call", model="ham", normalize="len", nproc=1) #' #' # To find the threshold cut, call findThreshold function for "gmm" method. #' output <- findThreshold(db$dist_nearest, method="gmm", model="norm-norm", cutoff="opt") #' print(output) #' #' # Plot results #' plotGmmThreshold(output, binwidth=0.02) #' } #' @export plotGmmThreshold <- function(data, cross=NULL, xmin=NULL, xmax=NULL, breaks=NULL, binwidth=NULL, title=NULL, size=1, silent=FALSE, ...) { # Define histogram data.frame and threshold xdf <- data.frame(x=data@x) # Generate curves gx <- seq(min(xdf$x), max(xdf$x), by=0.002) bits <- strsplit(data@model,'-')[[1]] if (bits[1] == "norm") { fit1 <- data.frame(x=gx, y=data@a1*dnorm(gx, mean=data@b1, sd=data@c1)) } else if (bits[1] == "gamma") { fit1 <- data.frame(x=gx, y=data@a1*dgamma(gx, shape=data@b1, scale=data@c1)) } if (bits[2] == "norm") { fit2 <- data.frame(x=gx, y=data@a2*dnorm(gx, mean = data@b2, sd=data@c2)) } else if (bits[2] == "gamma") { fit2 <- data.frame(x=gx, y=data@a2*dgamma(gx, shape = data@b2, scale=data@c2)) } # ggplot workaround if (is.null(xmin)) { xmin <- NA } if (is.null(xmax)) { xmax <- NA } # Plot distToNearest distribution plus Gaussian fits p <- ggplot(xdf, aes(x=!!rlang::sym("x"))) + baseTheme() + xlab("Distance") + ylab("Density") + geom_histogram(aes(y=after_stat(!!str2lang("density"))), binwidth=binwidth, fill="gray40", color="white") + geom_line(data=fit1, aes(x=!!rlang::sym("x"), y=!!rlang::sym("y")), color="darkslateblue", linewidth=size) + geom_line(data=fit2, aes(x=!!rlang::sym("x"), y=!!rlang::sym("y")), color="darkslateblue", linewidth=size) + geom_vline(xintercept=data@threshold, color="firebrick", linetype="longdash", linewidth=size) # Add cross histogram if (!is.null(cross)) { cdf <- data.frame(x=cross[is.finite(cross)]) p <- p + geom_histogram(data=cdf, aes(x=!!rlang::sym("x"), y=-after_stat(!!str2lang("density"))), binwidth=binwidth, fill="gray40", color="white", position="identity") + scale_y_continuous(labels=abs) } # Add x limits if (is.null(breaks) & (!is.na(xmin) | !is.na(xmax))) { p <- p + xlim(xmin, xmax) } # Set breaks if (!is.null(breaks)) { p <- p + scale_x_continuous(breaks=scales::pretty_breaks(n=breaks), limits=c(xmin, xmax)) } # Add Title if (!is.null(title)) { p <- p + ggtitle(title) } # Add additional theme elements p <- p + do.call(theme, list(...)) # Plot if (!silent) { plot(p) } else { return(p) } } #' Plot findThreshold results for the density method #' #' \code{plotDensityThreshold} plots the results from \code{"density"} method of #' \link{findThreshold}, including the smoothed density estimate, input nearest neighbor #' distance histogram, and threshold selected. #' #' @param data \link{DensityThreshold} object output by the \code{"density"} method #' of \link{findThreshold}. #' @param cross numeric vector of distances from \link{distToNearest} to draw as a #' histogram below the \code{data} histogram for comparison purposes. #' @param xmin minimum limit for plotting the x-axis. If \code{NULL} the limit will #' be set automatically. #' @param xmax maximum limit for plotting the x-axis. If \code{NULL} the limit will #' be set automatically. #' @param breaks number of breaks to show on the x-axis. If \code{NULL} the breaks will #' be set automatically. #' @param binwidth binwidth for the histogram. If \code{NULL} the binwidth #' will be set automatically to the bandwidth parameter determined by #' \link{findThreshold}. #' @param title string defining the plot title. #' @param size numeric value for the plot line sizes. #' @param silent if \code{TRUE} do not draw the plot and just return the ggplot2 #' object; if \code{FALSE} draw the plot. #' @param ... additional arguments to pass to ggplot2::theme. #' #' @return A ggplot object defining the plot. #' #' @seealso See \link{DensityThreshold} for the the input object definition and #' \link{findThreshold} for generating the input object. See #' \link{distToNearest} calculating nearest neighbor distances. #' #' @examples #' \donttest{ #' # Subset example data to one sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, sample_id == "-1h") #' #' # Use nucleotide Hamming distance and normalize by junction length #' db <- distToNearest(db, sequenceColumn="junction", vCallColumn="v_call_genotyped", #' jCallColumn="j_call", model="ham", normalize="len", nproc=1) #' #' # To find the threshold cut, call findThreshold function for "gmm" method. #' output <- findThreshold(db$dist_nearest, method="density") #' print(output) #' #' # Plot #' plotDensityThreshold(output) #' } #' @export plotDensityThreshold <- function(data, cross=NULL, xmin=NULL, xmax=NULL, breaks=NULL, binwidth=NULL, title=NULL, size=1, silent=FALSE, ...) { # Define plot data.frames xdf <- data.frame(x=data@x) ddf <- data.frame(x=data@xdens, y=data@ydens) ddf <- ddf[ddf$x > 0, ] # Set binwidth if (is.null(binwidth)) { binwidth <- data@bandwidth } # ggplot workaround if (is.null(xmin)) { xmin <- NA } if (is.null(xmax)) { xmax <- NA } # Plot distToNearest distribution plus Gaussian fits p <- ggplot(xdf, aes(x=!!rlang::sym("x"))) + baseTheme() + xlab("Distance") + ylab("Density") + geom_histogram(aes(y=after_stat(!!str2lang("density"))), binwidth=binwidth, fill="gray40", color="white") + geom_line(data=ddf, aes(x=!!rlang::sym("x"), y=!!rlang::sym("y")), color="darkslateblue", linewidth=size) + geom_vline(xintercept=data@threshold, color="firebrick", linetype="longdash", linewidth=size) # Add cross histogram if (!is.null(cross)) { cdf <- data.frame(x=cross[is.finite(cross)]) p <- p + geom_histogram(data=cdf, aes(x=!!rlang::sym("x"), y=-after_stat(!!str2lang("density"))), binwidth=binwidth, fill="gray40", color="white", position="identity") + scale_y_continuous(labels=abs) } # Add x limits if (is.null(breaks) & (!is.na(xmin) | !is.na(xmax))) { p <- p + coord_cartesian(xlim = c(xmin, xmax)) } # Set breaks if (!is.null(breaks)) { p <- p + scale_x_continuous(breaks=scales::pretty_breaks(n=breaks), limits=c(xmin, xmax)) } # Add title if (!is.null(title)) { p <- p + ggtitle(title) } # Add additional theme elements p <- p + do.call(theme, list(...)) # Plot if (!silent) { plot(p) } else { return(p) } } shazam/R/TargetingModels.R0000644000176200001440000036745114502021710015161 0ustar liggesusers# Targeting models #' @include Shazam.R #' @include Core.R NULL #### Data #### #' Uniform 5-mer null targeting model. #' #' A null 5-mer model of somatic hypermutation targeting where all substitution, mutability #' and targeting rates are uniformly distributed. #' #' @format A \link{TargetingModel} object. #' #' @seealso See \link{HH_S5F} and \link{HKL_S5F} for the human 5-mer targeting models; and #' \link{MK_RS5NF} for the mouse 5-mer targeting model. "U5N" #' Human heavy chain, silent, 1-mer, functional substitution model. #' #' 1-mer substitution model of somatic hypermutation based on analysis of silent mutations #' in functional heavy chain Ig sequences from Homo sapiens. #' #' @format A 4x4 matrix of nucleotide substitution rates. The rates are normalized, #' therefore each row sums up to 1. #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso See \link{HKL_S1F} for the human light chain 1-mer substitution model and #' \link{MK_RS1NF} for the mouse light chain 1-mer substitution model. #' #' @note \code{HH_S1F} replaces \code{HS1FDistance} in versions of SHazaM prior to 0.1.5. "HH_S1F" #' Human kappa and lambda chain, silent, 1-mer, functional substitution model. #' #' 1-mer substitution model of somatic hypermutation based on analysis of silent mutations #' in functional kappa and lambda light chain Ig sequences from Homo sapiens. #' #' @format A 4x4 matrix of nucleotide substitution rates. The rates are normalized, #' therefore each row sums up to 1. #' #' @references #' \enumerate{ #' \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, #' Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation #' Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of #' Immunology, 197(9), 3566-3574. #' } #' #' @seealso See \link{HH_S1F} for the human heavy chain 1-mer substitution model and #' \link{MK_RS1NF} for the mouse light chain 1-mer substitution model. #' #' @note Reported in Table III in Cui et al, 2016. "HKL_S1F" #' Mouse kappa chain, replacement and silent, 1-mer, non-functional substitution model. #' #' 1-mer substitution model of somatic hypermutation based on analysis of replacement and #' silent mutations in non-functional kappa light chain Ig sequences from NP-immunized Mus #' musculus. #' #' @format A 4x4 matrix of nucleotide substitution rates. The rates are normalized, #' therefore each row sums up to 1. #' #' @references #' \enumerate{ #' \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, #' Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation #' Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of #' Immunology, 197(9), 3566-3574. #' } #' #' @seealso See \link{HH_S1F} for the human heavy chain 1-mer substitution model and #' \link{HKL_S1F} for the human light chain 1-mer substitution model. #' #' @note \code{MK_RS1NF} replaces \code{M1NDistance} from versions of SHazaM prior to 0.1.5. "MK_RS1NF" #' Human heavy chain, silent, 5-mer, functional targeting model. #' #' 5-mer model of somatic hypermutation targeting based on analysis of silent mutations #' in functional heavy chain Ig sequences from Homo sapiens. #' #' @format A \link{TargetingModel} object. #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso See \link{HH_S1F} for the 1-mer substitution matrix from the same #' publication; \link{HKL_S5F} for the human light chain 5-mer targeting model; #' \link{MK_RS5NF} for the mouse 5-mer targeting model; and \link{U5N} for the #' uniform 5-mer null targeting model. "HH_S5F" #' Human kappa and lambda light chain, silent, 5-mer, functional targeting model. #' #' 5-mer model of somatic hypermutation targeting based on analysis of silent mutations #' in functional kappa and lambda light chain Ig sequences from Homo sapiens. #' #' @format A \link{TargetingModel} object. #' #' @references #' \enumerate{ #' \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, #' Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation #' Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of #' Immunology, 197(9), 3566-3574. #' } #' #' @seealso See \link{HH_S5F} for the human heavy chain 5-mer targeting model; #' \link{MK_RS5NF} for the mouse kappa light chain 5-mer targeting model; #' and \link{U5N} for the uniform 5-mer null targeting model. "HKL_S5F" #' Mouse kappa light chain, replacement and silent, 5-mer, non-functional targeting model. #' #' 5-mer model of somatic hypermutation targeting based on analysis of replacement and #' silent mutations in non-functional kappa light chain Ig sequences from NP-immunized #' Mus musculus. #' #' @format \link{TargetingModel} object. #' #' @references #' \enumerate{ #' \item Cui A, Di Niro R, Vander Heiden J, Briggs A, Adams K, Gilbert T, O'Connor K, #' Vigneault F, Shlomchik M and Kleinstein S (2016). A Model of Somatic Hypermutation #' Targeting in Mice Based on High-Throughput Ig Sequencing Data. The Journal of #' Immunology, 197(9), 3566-3574. #' } #' #' @seealso See \link{MK_RS1NF} for the 1-mer substitution matrix from the same #' publication; \link{HH_S5F} for the human heavy chain silent 5-mer #' functional targeting model; \link{HKL_S5F} for the human light chain #' silent 5-mer functional targeting model; and \link{U5N} for the #' uniform 5-mer null targeting model. "MK_RS5NF" #### Classes #### #' S4 class defining a mutability model #' #' \code{MutabilityModel} defines a data structure for the 5-mer motif-based SHM targeting #' mutability model. #' #' @slot .Data numeric vector containing 5-mer mutability estimates #' @slot source character vector annotating whether the mutability was #' inferred or directly measured. #' @slot numMutS a number indicating the number of silent mutations used for #' estimating mutability #' @slot numMutR a number indicating the number of replacement mutations used #' for estimating mutability #' #' @name MutabilityModel-class #' @rdname MutabilityModel-class #' @aliases MutabilityModel #' @exportClass MutabilityModel MutabilityModel <- setClass("MutabilityModel", slots=c(source="character", numMutS="numeric", numMutR="numeric"), contains="numeric") #' S4 class defining a targeting matrix #' #' \code{TargetingMatrix} defines a data structure for just the targeting matrix #' (as opposed to the entire \code{TargetingModel}) #' #' @slot .Data matrix. #' @slot numMutS number indicating the number of silent mutations used for #' estimating mutability. #' @slot numMutR number indicating the number of replacement mutations used #' for estimating mutability. #' #' @name TargetingMatrix-class #' @rdname TargetingMatrix-class #' @aliases TargetingMatrix #' @exportClass TargetingMatrix TargetingMatrix <- setClass("TargetingMatrix", slots=c(numMutS="numeric", numMutR="numeric"), contains="matrix") #' S4 class defining a targeting model #' #' \code{TargetingModel} defines a common data structure for mutability, substitution and #' targeting of immunoglobulin (Ig) sequencing data in a 5-mer microsequence context. #' #' @slot name Name of the model. #' @slot description Description of the model and its source data. #' @slot species Genus and species of the source sequencing data. #' @slot date Date the model was built. #' @slot citation Publication source. #' @slot substitution Normalized rates of the center nucleotide of a given 5-mer #' mutating to a different nucleotide. The substitution model #' is stored as a 5x3125 matrix of rates. Rows define #' the mutated nucleotide at the center of each 5-mer, one of #' \code{c("A", "C", "G", "T", "N")}, and columns define the #' complete 5-mer of the unmutated nucleotide sequence. #' @slot mutability Normalized rates of a given 5-mer being mutated. The #' mutability model is stored as a numeric vector of length 3125 #' with mutability rates for each 5-mer. Note that "normalized" #' means that the mutability rates for the 1024 5-mers that #' contain no "N" at any position sums up to 1 (as opposed to #' the entire vector summing up to 1). #' @slot targeting Rate matrix of a given mutation ocurring, defined as #' \eqn{mutability * substitution}. The targeting model #' is stored as a 5x3125 matrix. Rows define #' the mutated nucleotide at the center of each 5-mer, one of #' \code{c("A", "C", "G", "T", "N")}, and columns define the complete 5-mer #' of the unmutated nucleotide sequence. #' @slot numMutS number indicating the number of silent mutations used for #' estimating mutability. #' @slot numMutR number indicating the number of replacement mutations used #' for estimating mutability. #' #' @seealso See \link{createTargetingModel} building models from sequencing data. #' #' @name TargetingModel-class #' @rdname TargetingModel-class #' @aliases TargetingModel #' @exportClass TargetingModel setClass("TargetingModel", slots=c(name="character", description="character", species="character", date="character", citation="character", mutability="numeric", substitution="matrix", targeting="matrix", numMutS="numeric", numMutR="numeric"), prototype=list(name="name", description="description", species="species", date="2000-01-01", citation="citation", mutability=numeric(3125), substitution=matrix(0, 5, 3125), targeting=matrix(0, 5, 3125), numMutS=as.numeric(NA), numMutR=as.numeric(NA))) #### Methods #### #' @param x \code{MutabilityModel} object. #' #' @rdname MutabilityModel-class #' @aliases MutabilityModel-method #' @export setMethod("print", c(x="MutabilityModel"), function(x) { vec <- x@.Data; names(vec) <- names(x); print(vec) }) #' @param x \code{MutabilityModel} object. #' #' @rdname MutabilityModel-class #' @aliases MutabilityModel-method #' @export setMethod("as.data.frame", c(x="MutabilityModel"), function(x) { data.frame(motif=names(x), mutability=x, source=x@source[names(x)]) }) #' @param x \code{TargetingModel} object. #' @param y ignored. #' @param ... arguments to pass to \link{plotMutability}. #' #' @rdname TargetingModel-class #' @aliases TargetingModel-method #' @export setMethod("plot", c(x="TargetingModel", y="missing"), function(x, y, ...) { plotMutability(x, ...) }) #### Model building functions ##### #' Builds a substitution model #' #' \code{createSubstitutionMatrix} builds a 5-mer nucleotide substitution model by counting #' the number of substitution mutations occuring in the center position for all 5-mer #' motifs. #' #' @param db data.frame containing sequence data. #' @param model type of model to create. The default model, "s", #' builds a model by counting only silent mutations. \code{model="s"} #' should be used for data that includes functional sequences. #' Setting \code{model="rs"} creates a model by counting both #' replacement and silent mutations and may be used on fully #' non-functional sequence data sets. #' @param sequenceColumn name of the column containing IMGT-gapped sample sequences. #' @param germlineColumn name of the column containing IMGT-gapped germline sequences. #' @param vCallColumn name of the column containing the V-segment allele call. #' @param multipleMutation string specifying how to handle multiple mutations occuring #' within the same 5-mer. If \code{"independent"} then multiple #' mutations within the same 5-mer are counted indepedently. #' If \code{"ignore"} then 5-mers with multiple mutations are #' excluded from the total mutation tally. #' @param returnModel string specifying what type of model to return; one of #' \code{c("5mer", "1mer", "1mer_raw")}. If \code{"5mer"} #' (the default) then a 5-mer nucleotide context model is #' returned. If \code{"1mer"} or \code{"1mer_raw"} then a single #' nucleotide substitution matrix (no context) is returned; #' where \code{"1mer_raw"} is the unnormalized version of the #' \code{"1mer"} model. Note, neither 1-mer model may be used #' as input to \link{createMutabilityMatrix}. #' @param minNumMutations minimum number of mutations required to compute the 5-mer #' substitution rates. If the number of mutations for a 5-mer #' is below this threshold, its substitution rates will be #' estimated from neighboring 5-mers. Default is 50. #' Not required if \code{numMutationsOnly=TRUE}. #' @param numMutationsOnly when \code{TRUE}, return counting information on the number #' of mutations for each 5-mer, instead of building a substitution #' matrix. This option can be used for parameter tuning for #' \code{minNumMutations} during preliminary analysis. #' Default is \code{FALSE}. Only applies when \code{returnModel} #' is set to \code{"5mer"}. The \code{data.frame} returned when #' this argument is \code{TRUE} can serve as the input for #' \link{minNumMutationsTune}. #' #' @return For \code{returnModel = "5mer"}: #' #' When \code{numMutationsOnly} is \code{FALSE}, a 4x1024 matrix of column #' normalized substitution rates for each 5-mer motif with row names defining #' the center nucleotide, one of \code{c("A", "C", "G", "T")}, and column names #' defining the 5-mer nucleotide sequence. #' #' When \code{numMutationsOnly} is #' \code{TRUE}, a 1024x4 data frame with each row providing information on #' counting the number of mutations for a 5-mer. Columns are named #' \code{fivemer.total}, \code{fivemer.every}, \code{inner3.total}, and #' \code{inner3.every}, corresponding to, respectively, #' the total number of mutations when counted as a 5-mer, #' whether there is mutation to every other base when counted as a 5-mer, #' the total number of mutations when counted as an inner 3-mer, and #' whether there is mutation to every other base when counted as an inner 3-mer. #' #' For \code{returnModel = "1mer"} or \code{"1mer_raw"}: #' a 4x4 normalized or un-normalized 1-mer substitution matrix respectively. #' #' @details \strong{Caution: The targeting model functions do NOT support ambiguous #' characters in their inputs. You MUST make sure that your input and germline #' sequences do NOT contain ambiguous characters (especially if they are #' clonal consensuses returned from \code{collapseClones}).} #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso \link{extendSubstitutionMatrix}, \link{createMutabilityMatrix}, #' \link{createTargetingMatrix}, \link{createTargetingModel}, #' \link{minNumMutationsTune}. #' #' @examples #' \donttest{ #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:25,] #' #' # Count the number of mutations per 5-mer #' subCount <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' model="s", multipleMutation="independent", #' returnModel="5mer", numMutationsOnly=TRUE) #' #' # Create model using only silent mutations #' sub <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' model="s", multipleMutation="independent", #' returnModel="5mer", numMutationsOnly=FALSE, #' minNumMutations=20) #' } #' #' @export createSubstitutionMatrix <- function(db, model=c("s", "rs"), sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", multipleMutation=c("independent", "ignore"), returnModel=c("5mer", "1mer", "1mer_raw"), minNumMutations=50, numMutationsOnly=FALSE) { # Evaluate argument choices model <- match.arg(model) multipleMutation <- match.arg(multipleMutation) returnModel <- match.arg(returnModel) # Check for valid columns check <- checkColumns(db, c(sequenceColumn, germlineColumn, vCallColumn)) if (check != TRUE) { stop(check) } # Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn, germlineColumn)) # Check validity of input sequences # (MUST NOT CONTAIN AMBIGUOUS CHARACTERS -- not supported) bool_obsv <- checkAmbiguousExist(db[[sequenceColumn]]) bool_germ <- checkAmbiguousExist(db[[germlineColumn]]) if (any(bool_obsv | bool_germ)) { stop("Ambiguous characters are not supported in input sequences.") } # Setup nuc_chars <- NUCLEOTIDES[1:4] nuc_words <- seqinr::words(4, nuc_chars) # Define v_families (heavy or light chain) to only those found in the data v_families <- getFamily(db[[vCallColumn]]) # Define empty return list of lists substitutionMatrix <- matrix(0, ncol=4, nrow=4, dimnames=list(nuc_chars, nuc_chars)) substitutionList <- list() for(v_fam in unique(v_families)) { substitutionList[[v_fam]] <- list() for(word in nuc_words){ substitutionList[[v_fam]][[word]] <- substitutionMatrix } } # Remove IMGT gaps in the germline & input sequences matInputCollapsed <- removeCodonGaps(db[, c(sequenceColumn, germlineColumn)]) # TODO: Unnecessary conversion db[[sequenceColumn]] <- matInputCollapsed[, 1] db[[germlineColumn]] <- matInputCollapsed[, 2] # Get mutations mutations <- listObservedMutations(db, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, multipleMutation=multipleMutation, model=model) if (model == "s") { # Silent model for(index in 1:length(mutations)) { cSeq <- s2c(db[[sequenceColumn]][index]) cGL <- s2c(db[[germlineColumn]][index]) indexMutation <- mutations[[index]] v_fam <- v_families[index] positions <- as.numeric(names(indexMutation)) positions <- positions[positions<=VLENGTH] positions <- positions[!is.na(positions)] for( position in positions){ wrd <- c2s(c(cGL[(position-2):(position-1)],cGL[(position+1):(position+2)])) codonNucs <- getCodonPos(position) codonGL <- cGL[codonNucs] codonSeq <- cSeq[codonNucs] muCodonPos <- {position-1}%%3+1 seqAtMutation <- codonSeq[muCodonPos] glAtMutation <- codonGL[muCodonPos] if (!any(codonGL=="N") & !any(codonSeq=="N")) { codonPermutate <- matrix(rep(codonGL,3),ncol=3,byrow=T) codonPermutate[,muCodonPos] <- canMutateTo(glAtMutation)[-4] codonPermutate <- apply(codonPermutate,1,paste,collapse="") codonPermutate <- matrix( c( codonPermutate, rep(c2s(codonGL),3) ), ncol=2, byrow=F) # not intended to be used where input sequences have # ambiguous characters; it assumes that only 1 entry (r/s/stop/na) from # mutationType is non-zero/1 muType <- mutationTypeOptimized(codonPermutate) if (!length(grep("N",wrd))) { if (sum(muType=="s") == length(muType) ){ substitutionList[[v_fam]][[wrd]][glAtMutation,seqAtMutation] <- (substitutionList[[v_fam]][[wrd]][glAtMutation,seqAtMutation] + 1) } } } } } } else if (model == "rs") { # RS model (All mutations) for (index in 1:length(mutations)) { cSeq <- s2c(db[[sequenceColumn]][index]) cGL <- s2c(db[[germlineColumn]][index]) indexMutation <- mutations[[index]] v_fam <- v_families[index] positions <- as.numeric(names(indexMutation)) positions <- positions[positions<=VLENGTH] positions <- positions[!is.na(positions)] for( position in positions){ wrd <- c2s(c(cGL[(position-2):(position-1)],cGL[(position+1):(position+2)])) codonNucs <- getCodonPos(position) codonGL <- cGL[codonNucs] codonSeq <- cSeq[codonNucs] muCodonPos <- {position-1}%%3+1 seqAtMutation <- codonSeq[muCodonPos] glAtMutation <- codonGL[muCodonPos] if( !any(codonGL=="N") & !any(codonSeq=="N") ){ if(!length(grep("N",wrd))){ substitutionList[[v_fam]][[wrd]][glAtMutation,seqAtMutation] <- substitutionList[[v_fam]][[wrd]][glAtMutation, seqAtMutation] + 1 } } } } } # Convert substitutionList to listSubstitution to facilitate the aggregation of mutations arrNames <- c(outer(unique(v_families), nuc_words, paste, sep = "_")) listSubstitution <- array(0, dim=c(length(arrNames), 4, 4), dimnames=list(arrNames, nuc_chars, nuc_chars)) for(v_fam in unique(v_families)){ listSubstitution[paste(v_fam, nuc_words, sep="_"), , ] <- t(sapply(nuc_words, function(word) { substitutionList[[v_fam]][[word]] })) } # Aggregate mutations from all V families M <- list() subMat1mer <- matrix(0, 4, 4) # a single substitution matrix for all fivemers listSubNames <- sapply(dimnames(listSubstitution)[[1]], function(x) { strsplit(x, "_", fixed=TRUE)[[1]] }) .sumSub <- function(i, n) { x <- listSubstitution[listSubNames[2, ] == n, i, ] if(is.null(dim(x))) { return (x) } else { return (colSums(x)) } } for (nuc_word in nuc_words) { # Sums mutations from all families M[[nuc_word]] <- t(sapply(1:4, .sumSub, n=nuc_word)) rownames(M[[nuc_word]]) <- nuc_chars subMat1mer <- subMat1mer + M[[nuc_word]] } # Return 1-mer substitution model; this output cannot be used for createMutabilityMatrix if (returnModel == "1mer") { subMat1merNorm <- t(apply(subMat1mer, 1, function(x){x/sum(x)})) return (subMat1merNorm) } else if (returnModel == "1mer_raw") { return (subMat1mer) } ##### for a given 5mer, count number of mutations # fivemer=M; FIVEMER="CCATT" .simplifivemer <- function(fivemer, FIVEMER, Thresh=50, count=F) { # center Nuc=substr(FIVEMER,3,3) # neighbors Nei=paste(substr(FIVEMER,1,2),substr(FIVEMER,4,5),collapse="",sep="") ### using 5mer # aggregate mutations FIVE.5 <- fivemer[[Nei]][Nuc,] # count total number of mutations for a given 5mer fivemer.total <- sum(FIVE.5) # are there mutations to every other base? fivemer.every <- ( sum(FIVE.5==0)==1 ) ### using inner 3mer # aggregate mutations from 5-mers with the same inner 3-mer FIVE.3 <- FIVE.5 for(i in 1:4){ for(j in 1:4){ MutatedNeighbor=paste(nuc_chars[i],substring(Nei,2,3),nuc_chars[j],collapse="",sep="") FIVE.3=FIVE.3+fivemer[[MutatedNeighbor]][Nuc,] } } # count total number of mutations for inner 3mer inner3.total <- sum(FIVE.3) # are there mutations to every other base? inner3.every <- ( sum(FIVE.3==0)==1 ) ### using 1mer FIVE.1 <- FIVE.5 MutatedNeighbors <- seqinr::words(4, nuc_chars) for (MutatedNeighbor in MutatedNeighbors) { FIVE.1=FIVE.1+fivemer[[MutatedNeighbor]][Nuc,] } if (!count) { # For a 5mer, if the total number of mutations is greater than Thresh, # and if there are mutations to every other base, compute for the 5mer if ( fivemer.total > Thresh & fivemer.every ){ return(FIVE.5) } else if ( inner3.total > Thresh & inner3.every ) { # Otherwise aggregate mutations from 5-mers with the same inner 3-mer return(FIVE.3) } else { # If the total number of mutations is still not enough, # aggregate mutations from all 5-mers (i.e., use 1-mer model) return(FIVE.1) } } return(data.frame(fivemer.total, fivemer.every, inner3.total, inner3.every, stringsAsFactors=F)) } # either construct 5mer substition matrix, and normalize (numMutationsOnly = F) if (!numMutationsOnly) { substitutionModel <- sapply(seqinr::words(5, nuc_chars), function(x) { .simplifivemer(M, x, Thresh = minNumMutations, count = numMutationsOnly) }, simplify=T) # Assign A->A, C->C, G->G, T->T to NA center_nuc <- gsub("..([ACGT])..", "\\1", colnames(substitutionModel)) for (i in 1:length(center_nuc)) { substitutionModel[center_nuc[i], i] <- NA } # Normalize by column substitutionModel <- apply(substitutionModel, 2, function(x) { x / sum(x, na.rm=TRUE) }) substitutionModel[!is.finite(substitutionModel)] <- NA } else { # or count number of mutations (numMutationsOnly = T), return data frame # need to set simplify to F in sapply() and then use bind_rows; otherwise # every entry in df would be a list substitutionModel <- sapply(seqinr::words(5, nuc_chars), function(x) { .simplifivemer(M, x, Thresh = minNumMutations, count = numMutationsOnly) }, simplify=F) substitutionModel <- dplyr::bind_rows(substitutionModel) rownames(substitutionModel) <- seqinr::words(5, nuc_chars) } return(substitutionModel) } #' Parameter tuning for minNumMutations #' #' \code{minNumMutationsTune} helps with picking a threshold value for \code{minNumMutations} #' in \link{createSubstitutionMatrix} by tabulating the number of 5-mers for which #' substitution rates would be computed directly or inferred at various threshold values. #' #' @param subCount \code{data.frame} returned by \link{createSubstitutionMatrix} #' with \code{numMutationsOnly=TRUE}. #' @param minNumMutationsRange a number or a vector indicating the value or range of values #' of \code{minNumMutations} to try. #' #' @return A 3xn \code{matrix}, where n is the number of trial values of \code{minNumMutations} #' supplied in \code{minNumMutationsRange}. Each column corresponds to a value #' in \code{minNumMutationsRange}. The rows correspond to the number of 5-mers #' for which substitution rates would be computed directly using the 5-mer itself #' (\code{"5mer"}), using its inner 3-mer (\code{"3mer"}), and using the central #' 1-mer (\code{"1mer"}), respectively. #' #' @details At a given threshold value of \code{minNumMutations}, for a given 5-mer, #' if the total number of mutations is greater than the threshold and there #' are mutations to every other base, substitution rates are computed directly #' for the 5-mer using its mutations. Otherwise, mutations from 5-mers with #' the same inner 3-mer as the 5-mer of interest are aggregated. If the number #' of such mutations is greater than the threshold and there are mutations to #' every other base, these mutations are used for inferring the substitution #' rates for the 5-mer of interest; if not, mutations from all 5-mers with the #' same center nucleotide are aggregated and used for inferring the substitution #' rates for the 5-mer of interest (i.e. the 1-mer model). #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso See argument \code{numMutationsOnly} in \link{createSubstitutionMatrix} #' for generating the required input \code{data.frame} \code{subCount}. #' See argument \code{minNumMutations} in \link{createSubstitutionMatrix} #' for what it does. #' #' @examples #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") #' #' # Count the number of mutations per 5-mer #' subCount <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' model="s", multipleMutation="independent", #' returnModel="5mer", numMutationsOnly=TRUE) #' #' # Tune minNumMutations #' minNumMutationsTune(subCount, seq(from=10, to=80, by=10)) #' #' @export minNumMutationsTune <- function(subCount, minNumMutationsRange) { stopifnot( nrow(subCount)==1024 & ncol(subCount)==4 ) tuneMtx <- sapply(minNumMutationsRange, function(thresh) { method.count <- c(# as 5mer sum( subCount$fivemer.total > thresh & subCount$fivemer.every ), # as inner 3mer sum( !(subCount$fivemer.total > thresh & subCount$fivemer.every) & (subCount$inner3.total > thresh & subCount$inner3.every) ), # as 1mer sum( !(subCount$fivemer.total > thresh & subCount$fivemer.every) & !(subCount$inner3.total > thresh & subCount$inner3.every) ) ) names(method.count) <- c("5mer", "3mer", "1mer") stopifnot( sum(method.count)==1024 ) return(method.count) }) colnames(tuneMtx) <- minNumMutationsRange return(tuneMtx) } #' Builds a mutability model #' #' \code{createMutabilityMatrix} builds a 5-mer nucleotide mutability model by counting #' the number of mutations occuring in the center position for all 5-mer motifs. #' #' @param db data.frame containing sequence data. #' @param substitutionModel matrix of 5-mer substitution rates built by #' \link{createSubstitutionMatrix}. Note, this model will #' only impact mutability scores when \code{model="s"} #' (using only silent mutations). #' @param model type of model to create. The default model, "s", #' builds a model by counting only silent mutations. \code{model="s"} #' should be used for data that includes functional sequences. #' Setting \code{model="rs"} creates a model by counting both #' replacement and silent mutations and may be used on fully #' non-functional sequence data sets. #' @param sequenceColumn name of the column containing IMGT-gapped sample sequences. #' @param germlineColumn name of the column containing IMGT-gapped germline sequences. #' @param vCallColumn name of the column containing the V-segment allele call. #' @param multipleMutation string specifying how to handle multiple mutations occuring #' within the same 5-mer. If \code{"independent"} then multiple #' mutations within the same 5-mer are counted indepedently. #' If \code{"ignore"} then 5-mers with multiple mutations are #' excluded from the total mutation tally. #' @param minNumSeqMutations minimum number of mutations in sequences containing each 5-mer #' to compute the mutability rates. If the number is smaller #' than this threshold, the mutability for the 5-mer will be #' inferred. Default is 500. Not required if #' \code{numSeqMutationsOnly=TRUE}. #' @param numSeqMutationsOnly when \code{TRUE}, return only a vector counting the number of #' observed mutations in sequences containing each 5-mer. This #' option can be used for parameter tuning for \code{minNumSeqMutations} #' during preliminary analysis using \link{minNumSeqMutationsTune}. #' Default is \code{FALSE}. #' #' @return When \code{numSeqMutationsOnly} is \code{FALSE}, a \code{MutabilityModel} containing a #' named numeric vector of 1024 normalized mutability rates for each 5-mer motif with names #' defining the 5-mer nucleotide sequence. #' #' When \code{numSeqMutationsOnly} is \code{TRUE}, a named numeric #' vector of length 1024 counting the number of observed mutations in sequences containing #' each 5-mer. #' #' @details \strong{Caution: The targeting model functions do NOT support ambiguous #' characters in their inputs. You MUST make sure that your input and germline #' sequences do NOT contain ambiguous characters (especially if they are #' clonal consensuses returned from \code{collapseClones}).} #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso \link{MutabilityModel}, \link{extendMutabilityMatrix}, \link{createSubstitutionMatrix}, #' \link{createTargetingMatrix}, \link{createTargetingModel}, #' \link{minNumSeqMutationsTune} #' #' @examples #' \donttest{ #' # Subset example data to 50 sequences of one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:50,] #' #' # Create model using only silent mutations #' sub_model <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call",model="s") #' mut_model <- createMutabilityMatrix(db, sub_model, model="s", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' minNumSeqMutations=200, #' numSeqMutationsOnly=FALSE) #' #' # View top 5 mutability estimates #' head(sort(mut_model, decreasing=TRUE), 5) #' #' # View the number of S mutations used for estimating mutabilities #' mut_model@numMutS #' #' # Count the number of mutations in sequences containing each 5-mer #' mut_count <- createMutabilityMatrix(db, sub_model, model="s", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' numSeqMutationsOnly=TRUE) #' } #' #' @export createMutabilityMatrix <- function(db, substitutionModel, model=c("s", "rs"), sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", multipleMutation=c("independent", "ignore"), minNumSeqMutations=500, numSeqMutationsOnly=FALSE) { # substitutionModel=sub_model; model="s"; sequenceColumn="sequence_alignment"; germlineColumn="germline_alignment_d_mask" # vCallColumn="v_call"; multipleMutation="ignore"; minNumSeqMutations=10 # Evaluate argument choices model <- match.arg(model) multipleMutation <- match.arg(multipleMutation) # Check for valid columns check <- checkColumns(db, c(sequenceColumn, germlineColumn, vCallColumn)) if (check != TRUE) { stop(check) } # Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn, germlineColumn)) # Check validity of input sequences # (MUST NOT CONTAIN AMBIGUOUS CHARACTERS -- not supported) bool_obsv <- checkAmbiguousExist(db[[sequenceColumn]]) bool_germ <- checkAmbiguousExist(db[[germlineColumn]]) if (any(bool_obsv | bool_germ)) { stop("Ambiguous characters are not supported in input sequences.") } # Check that the substitution model is valid if (any(dim(substitutionModel) != c(4, 1024))) { stop ("Please supply a valid 5-mer substitutionModel.") } # Set constants for function nuc_chars <- NUCLEOTIDES[1:4] # Remove IMGT gaps in the germline & input sequences matInputCollapsed <- removeCodonGaps(db[, c(sequenceColumn, germlineColumn)]) # TODO: Unnecessary conversion db[[sequenceColumn]] <- matInputCollapsed[, 1] db[[germlineColumn]] <- matInputCollapsed[, 2] # Count mutations # TODO: this could be listMutations() instead, and skip the conversion from matInputCollapsed back to a data.frame mutations <- listObservedMutations(db, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, multipleMutation=multipleMutation, model=model) # Foreground Count: Count the number of observed mutations for each 5-mer template <- rep(0, 1024) names(template) <- seqinr::words(5, nuc_chars) COUNT <- list() for(index in 1:length(mutations)){ COUNT[[index]] <- template indexMutation <- mutations[[index]] if(!sum(is.na(indexMutation))){ cSeq <- s2c(db[[sequenceColumn]][index]) cGL <- s2c(db[[germlineColumn]][index]) positions <- as.numeric(names(indexMutation)) positions <- positions[positions <= VLENGTH] positions <- positions[!is.na(positions)] for (position in positions){ wrd5 <- substr(db[[germlineColumn]][index], position - 2, position + 2) if(!grepl("[^ACGT]", wrd5) & nchar(wrd5) == 5){ codonNucs <- getCodonPos(position) codonGL <- cGL[codonNucs] codonSeq <- cSeq[codonNucs] muCodonPos <- {position - 1} %% 3 + 1 #seqAtMutation <- codonSeq[muCodonPos] glAtMutation <- codonGL[muCodonPos] if (!any(codonGL %in% c("N", "-", ".")) & !any(codonSeq %in% c("N", "-", "."))) { if (!length(grep("N", wrd5))) { COUNT[[index]][wrd5]<- COUNT[[index]][wrd5] + 1; } } } } } } # Define sum of rates for nucleotide sets from substitution model # Two character sets wrd2Index <- combn(1:4, 2) wrd2Sums <- t(apply(wrd2Index, 2, function(x) colSums(substitutionModel[x, ], na.rm=TRUE))) rownames(wrd2Sums) <- apply(wrd2Index, 2, function(x) paste(nuc_chars[x], collapse="")) # Three character sets wrd3Index <- combn(1:4, 3) wrd3Sums <- t(apply(wrd3Index, 2, function(x) colSums(substitutionModel[x, ], na.rm=TRUE))) rownames(wrd3Sums) <- apply(wrd3Index, 2, function(x) paste(nuc_chars[x], collapse="")) # Merge single character, two character and three character sets substitutionSums <- rbind(substitutionModel, wrd2Sums, wrd3Sums) # Replace dots with Ns sSeqVec <- gsub("\\.", "N", db[[sequenceColumn]]) sGermVec <- gsub("\\.", "N", db[[germlineColumn]]) # Define template for 5-mer sums by position countTemplate <- matrix(0, VLENGTH, 1024, dimnames=list(1:VLENGTH, names(template))) # Background Count: Count the number of occurrences of each 5-mer BG_COUNT <- list() for (index in 1:length(mutations)) { tmpCounts <- countTemplate sGL <- sGermVec[index] cSeq <- s2c(sSeqVec[index]) cGL <- s2c(sGL)[1:VLENGTH] positions <- 3:(length(cGL) - 2) for (pos in positions) { wrd5 <- substr(sGL, pos - 2, pos + 2) if (!grepl("[^ACGT]", wrd5) & nchar(wrd5) == 5) { codonNucs <- getCodonPos(pos) codonGL <- cGL[codonNucs] codonSeq <- cSeq[codonNucs] muCodonPos <- (pos - 1) %% 3 + 1 glAtMutation <- codonGL[muCodonPos] if (!any(codonGL %in% c("N", "-")) & !any(codonSeq %in% c("N", "-"))) { # Determine mutation types for NUCLEOTIDES[1:4] muType <- CODON_TABLE[1:4 + 4*(muCodonPos - 1), stri_flatten(codonGL)] # Set characters that meet mutation criteria if (model == "s") { muChars <- nuc_chars[1:4][nuc_chars[1:4] != glAtMutation & muType == "s"] } else { muChars <- nuc_chars[1:4][nuc_chars[1:4] != glAtMutation] } # Update counts if (length(muChars) > 0) { #cat(stri_flatten(muChars), substitutionSums[stri_flatten(muChars), wrd5], "\n") tmpCounts[pos, wrd5] <- substitutionSums[stri_flatten(muChars), wrd5] } } } } BG_COUNT[[index]] <- colSums(tmpCounts) BG_COUNT[[index]][BG_COUNT[[index]] == 0] <- NA } Mutability <- list() for(i in 1:length(mutations)) { mut_mat <- COUNT[[i]] / BG_COUNT[[i]] mut_mat <- mut_mat / sum(mut_mat, na.rm=TRUE) mut_mat[!is.finite(mut_mat)] <- NA wgt_mat <- length(mutations[[i]]) Mutability[[i]] <- list(mut_mat, wgt_mat) } # total counts of mutations # each list item is the total S and R mutation counts in a seq mutationsTotalLst <- lapply(mutations, function(m){ return( c(S=sum(m=="s", na.rm=T), R=sum(m=="r", na.rm=T)) ) }) # total S and R mutation counts across seqs mutationsTotalRS <- Reduce("+", mutationsTotalLst) # Aggregate mutability MutabilityMatrix <- sapply(Mutability, function(x) x[[1]]) MutabilityWeights <- sapply(Mutability, function(x) x[[2]]) Mutability_Mean <- apply(MutabilityMatrix, 1, weighted.mean, w=MutabilityWeights, na.rm=TRUE) Mutability_Mean[!is.finite(Mutability_Mean)] <- NA Mutability_Mean[Mutability_Mean == 0] <- NA # Filter out 5-mers with low number of observed mutations in the sequences NumSeqMutations <- sapply(1:1024,function(i)sum(MutabilityWeights[!is.na(MutabilityMatrix[i,])])) names(NumSeqMutations) <- names(Mutability_Mean) if (numSeqMutationsOnly) {return(NumSeqMutations)} Mutability_Mean[NumSeqMutations <= minNumSeqMutations] <- NA # Infer mutability for missing 5-mers .fillHot <-function(FIVEMER,mutability){ if(FIVEMER%in%names(mutability))if(!is.na(mutability[[FIVEMER]]))if(mutability[[FIVEMER]]>=0.0)return(mutability[[FIVEMER]]) Nuc=substr(FIVEMER,3,3) #Nei=paste(substr(FIVEMER,1,2),substr(FIVEMER,4,5),collapse="",sep="") FIVE=0 COUNT=0 # For A/T, infer mutability using the 3-mer model. if(Nuc%in%c("A","T")){ for(i in 1:3){ for(j in 1:3){ MutatedNeighbor=paste(canMutateTo(substr(FIVEMER,1,1))[i],substr(FIVEMER,2,4),canMutateTo(substr(FIVEMER,5,5))[j],collapse="",sep="") if(!is.na(mutability[[MutatedNeighbor]])){ FIVE=FIVE+mutability[[MutatedNeighbor]] COUNT=COUNT+1 } } } return(FIVE/COUNT) } # For G, infer using 5-mers with the same downstream nucleotides if(Nuc=="G"){ for(i in 1:3){ for(j in 1:3){ MutatedNeighbor=paste(canMutateTo(substr(FIVEMER,1,1))[i],canMutateTo(substr(FIVEMER,2,2))[j],substr(FIVEMER,3,5),collapse="",sep="") if(!is.na(mutability[[MutatedNeighbor]])){ FIVE=FIVE+mutability[[MutatedNeighbor]] COUNT=COUNT+1 } } } return(FIVE/COUNT) } # For C, infer using 5-mers with the same upstream nucleotides if(Nuc=="C"){ for(i in 1:3){ for(j in 1:3){ MutatedNeighbor=paste(substr(FIVEMER,1,3),canMutateTo(substr(FIVEMER,4,4))[i],canMutateTo(substr(FIVEMER,5,5))[j],collapse="",sep="") if(!is.na(mutability[[MutatedNeighbor]])){ FIVE=FIVE+mutability[[MutatedNeighbor]] COUNT=COUNT+1 } } } return(FIVE/COUNT) } } Mutability_Mean_Complete <-sapply(words(5, nuc_chars), .fillHot, mutability = Mutability_Mean) for(i in names(which(is.na(Mutability_Mean_Complete)))){ Mutability_Mean_Complete[i]<- .fillHot(i,mutability=Mutability_Mean_Complete) } for(i in names(which((Mutability_Mean_Complete)<1e-6))){ Mutability_Mean_Complete[i]<- .fillHot(i,mutability=Mutability_Mean_Complete) } # If the neighboring 5-mers still don't have enough mutations, use 0 instead. if (length(is.na(Mutability_Mean_Complete)) > 0) { warning("Insufficient number of mutations to infer some 5-mers. Filled with 0. ") Mutability_Mean_Complete[is.na(Mutability_Mean_Complete)] <- 0 } # Normalize Mutability_Mean_Complete <- Mutability_Mean_Complete / sum(Mutability_Mean_Complete, na.rm=TRUE) # Define whether the 5-mer mutability is measured or inferred mut_names <- names(Mutability_Mean_Complete) mut_source <- setNames(rep("Measured", length(mut_names)), mut_names) mut_source[mut_names %in% names(which(is.na(Mutability_Mean)))] <- "Inferred" # Return MutabilityModel mut_model <- MutabilityModel(Mutability_Mean_Complete, source=mut_source, numMutS=mutationsTotalRS[["S"]], numMutR=mutationsTotalRS[["R"]]) return(mut_model) } #' Parameter tuning for minNumSeqMutations #' #' \code{minNumSeqMutationsTune} helps with picking a threshold value for \code{minNumSeqMutations} #' in \link{createMutabilityMatrix} by tabulating the number of 5-mers for which #' mutability would be computed directly or inferred at various threshold values. #' #' @param mutCount a \code{vector} of length 1024 returned by #' \link{createMutabilityMatrix} with \code{numSeqMutationsOnly=TRUE}. #' @param minNumSeqMutationsRange a number or a vector indicating the value or the range of values #' of \code{minNumSeqMutations} to try. #' #' @return A 2xn \code{matrix}, where n is the number of trial values of \code{minNumSeqMutations} #' supplied in \code{minNumSeqMutationsRange}. Each column corresponds to a value #' in \code{minNumSeqMutationsRange}. The rows correspond to the number of 5-mers #' for which mutability would be computed directly (\code{"measured"}) and inferred #' (\code{"inferred"}), respectively. #' #' @details At a given threshold value of \code{minNumSeqMutations}, for a given 5-mer, #' if the total number of mutations is greater than the threshold, mutability #' is computed directly. Otherwise, mutability is inferred. #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso See argument \code{numSeqMutationsOnly} in \link{createMutabilityMatrix} #' for generating the required input \code{vector} \code{mutCount}. #' See argument \code{minNumSeqMutations} in \link{createMutabilityMatrix} #' for what it does. #' #' @examples #' \donttest{ #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") #' set.seed(112) #' db <- dplyr::slice_sample(db, n=75) #' # Create model using only silent mutations #' sub <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' model="s", multipleMutation="independent", #' returnModel="5mer", numMutationsOnly=FALSE, #' minNumMutations=20) #' #' # Count the number of mutations in sequences containing each 5-mer #' mutCount <- createMutabilityMatrix(db, substitutionModel = sub, #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' model="s", multipleMutation="independent", #' numSeqMutationsOnly=TRUE) #' #' # Tune minNumSeqMutations #' minNumSeqMutationsTune(mutCount, seq(from=100, to=300, by=50)) #' } #' @export minNumSeqMutationsTune <- function(mutCount, minNumSeqMutationsRange) { stopifnot( length(mutCount) == 1024 ) tuneMtx <- sapply(minNumSeqMutationsRange, function(thresh) { method.count <- c( sum(mutCount > thresh), sum(mutCount <= thresh) ) names(method.count) <- c("measured", "inferred") stopifnot( sum(method.count)==1024 ) return(method.count) }) colnames(tuneMtx) <- minNumSeqMutationsRange return(tuneMtx) } #' Extends a substitution model to include Ns. #' #' \code{extendSubstitutionMatrix} extends a 5-mer nucleotide substitution model #' with 5-mers that include Ns by averaging over all corresponding 5-mers without Ns. #' #' @param substitutionModel matrix of 5-mers substitution counts built by #' \link{createSubstitutionMatrix}. #' #' @return A 5x3125 matrix of normalized substitution rate for each 5-mer motif with #' rows names defining the center nucleotide, one of \code{c("A", "C", "G", "T", "N")}, #' and column names defining the 5-mer nucleotide sequence. #' #' @seealso \link{createSubstitutionMatrix}, \link{extendMutabilityMatrix} #' #' @examples #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") #' #' # Create model using only silent mutations #' sub_model <- createSubstitutionMatrix(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call",model="s") #' ext_model <- extendSubstitutionMatrix(sub_model) #' #' @export extendSubstitutionMatrix <- function(substitutionModel) { # TODO: fix order so Ns are at the end? (c(input_names, words not in input_names)) # Define old and new column/row names input_names <- colnames(substitutionModel) nuc_chars <- NUCLEOTIDES[1:5] nuc_5mers <- seqinr::words(5, alphabet=nuc_chars) # Define empty extended matrix with Ns extend_mat <- matrix(NA, nrow=length(nuc_chars), ncol=length(nuc_5mers), dimnames=list(nuc_chars, nuc_5mers)) # Extend matrix with Ns for (mer in nuc_5mers) { if (mer %in% input_names) { extend_mat[, mer] <- c(substitutionModel[, mer], "N"=NA) } else { mer_char <- s2c(mer) n_index <- grep("N", mer_char) if (any(n_index == 3)) { extend_mat[, mer] <- NA } else { mer_char[n_index] <- "." mer_str <- c2s(mer_char) mer_index <- grep(mer_str, input_names) extend_mat[, mer] <- c(apply(substitutionModel[, mer_index], 1, mean, na.rm=TRUE), "N"=NA) } } } # Normalize #extend_mat <- apply(extend_mat, 2, function(x) { x/sum(x, na.rm=TRUE) }) extend_mat[!is.finite(extend_mat)] <- NA return (extend_mat) } #' Extends a mutability model to include Ns. #' #' \code{extendMutabilityMatrix} extends a 5-mer nucleotide mutability model #' with 5-mers that include Ns by averaging over all corresponding 5-mers without Ns. #' #' @param mutabilityModel vector of 5-mer mutability rates built by #' \link{createMutabilityMatrix}. #' #' @return A \code{MutabilityModel} containing a 3125 vector of normalized #' mutability rates for each 5-mer motif with names defining the 5-mer #' nucleotide sequence. Note that "normalized" means that the mutability #' rates for the 1024 5-mers that contain no "N" at any position sums up #' to 1 (as opposed to the entire vector summing up to 1). #' #' If the input \code{mutabilityModel} is of class \code{MutabilityModel}, #' then the output \code{MutabilityModel} will carry over the input #' \code{numMutS} and \code{numMutR} slots. #' #' @seealso \link{createMutabilityMatrix}, \link{extendSubstitutionMatrix}, #' \link{MutabilityModel} #' #' @examples #' \donttest{ #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") #' set.seed(112) #' db <- dplyr::slice_sample(db, n=75) #' #' # Create model using only silent mutations and ignore multiple mutations #' sub_model <- createSubstitutionMatrix(db, model="s", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call") #' mut_model <- createMutabilityMatrix(db, sub_model, model="s", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call") #' ext_model <- extendMutabilityMatrix(mut_model) #' } #' #' @export extendMutabilityMatrix <- function(mutabilityModel) { # TODO: fix order so Ns are at the end? (c(input_names, words not in input_names)) # Define old and new column/row names input_names <- names(mutabilityModel) nuc_chars <- NUCLEOTIDES[1:5] nuc_5mers <- seqinr::words(5, alphabet=nuc_chars) # Define empty extended matrix with Ns extend_mat <- array(NA, dim=length(nuc_5mers), dimnames=list(nuc_5mers)) # Extend matrix with Ns for(mer in nuc_5mers) { #cat(mer,"\n") if (mer %in% input_names) { extend_mat[mer] <- mutabilityModel[mer] } else { mer_char <- s2c(mer) n_index <- grep("N", mer_char) if (any(n_index == 3)) { extend_mat[mer] <- NA } else { mer_char[n_index] <- "." mer_str <- c2s(mer_char) mer_index <- grep(mer_str, input_names) extend_mat[mer] <- mean(mutabilityModel[mer_index], na.rm=TRUE) } } } # Normalize #extend_mat <- extend_mat / sum(extend_mat, na.rm=TRUE) extend_mat[!is.finite(extend_mat)] <- NA # Carry over @numMutS and @numMutR, if any if (all(c("numMutS", "numMutR") %in% slotNames(mutabilityModel))) { mut_s <- mutabilityModel@numMutS mut_r <- mutabilityModel@numMutR } else { mut_s <- as.numeric(NA) mut_r <- as.numeric(NA) } # Carry over @source if ("source" %in% slotNames(mutabilityModel)) { mut_names <- names(extend_mat) mut_source <- setNames(rep("Extended", length(mut_names)), mut_names) mut_source[names(mutabilityModel@source)] <- mutabilityModel@source } else { mut_source <- as.character(NA) } # Return extended MutabilityModel extend_model <- MutabilityModel(extend_mat, source=mut_source, numMutS=mut_s, numMutR=mut_r) return(extend_model) } #' Calculates a targeting rate matrix #' #' \code{createTargetingMatrix} calculates the targeting model matrix as the #' combined probability of mutability and substitution. #' #' @param substitutionModel matrix of 5-mers substitution rates built by #' \link{createSubstitutionMatrix} or #' \link{extendSubstitutionMatrix}. #' @param mutabilityModel vector of 5-mers mutability rates built by #' \link{createMutabilityMatrix} or #' \link{extendMutabilityMatrix}. #' #' @return A \code{TargetingMatrix} with the same dimensions as the input \code{substitutionModel} #' containing normalized targeting probabilities for each 5-mer motif with #' row names defining the center nucleotide and column names defining the #' 5-mer nucleotide sequence. #' #' If the input \code{mutabilityModel} is of class \code{MutabilityModel}, then the output #' \code{TargetingMatrix} will carry over the input \code{numMutS} and \code{numMutR} slots. #' #' @details #' Targeting rates are calculated by multiplying the normalized mutability rate by the #' normalized substitution rates for each individual 5-mer. #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso \link{createSubstitutionMatrix}, \link{extendSubstitutionMatrix}, #' \link{createMutabilityMatrix}, \link{extendMutabilityMatrix}, #' \link{TargetingMatrix}, \link{createTargetingModel} #' #' @examples #' \donttest{ #' # Subset example data to 50 sequences, of one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:50,] #' #' # Create 4x1024 models using only silent mutations #' sub_model <- createSubstitutionMatrix(db, model="s", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call") #' mut_model <- createMutabilityMatrix(db, sub_model, model="s", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call") #' #' # Extend substitution and mutability to including Ns (5x3125 model) #' sub_model <- extendSubstitutionMatrix(sub_model) #' mut_model <- extendMutabilityMatrix(mut_model) #' #' # Create targeting model from substitution and mutability #' tar_model <- createTargetingMatrix(sub_model, mut_model) #' } #' #' @export createTargetingMatrix <- function(substitutionModel, mutabilityModel) { # Calculate targeting tar_mat <- sweep(substitutionModel, 2, mutabilityModel, `*`) # Normalize #tar_mat <- tar_mat / sum(tar_mat, na.rm=TRUE) tar_mat[!is.finite(tar_mat)] <- NA # carry over @numMutS and @numMutR, if any if (all(c("numMutS", "numMutR") %in% slotNames(mutabilityModel))) { tar_mat <- TargetingMatrix(tar_mat, numMutS=mutabilityModel@numMutS, numMutR=mutabilityModel@numMutR) } else { tar_mat <- TargetingMatrix(tar_mat, # NA is of class logical by default numMutS=as.numeric(NA), numMutR=as.numeric(NA)) } return(tar_mat) } #' Creates a TargetingModel #' #' \code{createTargetingModel} creates a 5-mer \code{TargetingModel}. #' #' @param db data.frame containing sequence data. #' @param model type of model to create. The default model, "s", #' builds a model by counting only silent mutations. \code{model="s"} #' should be used for data that includes functional sequences. #' Setting \code{model="rs"} creates a model by counting both #' replacement and silent mutations and may be used on fully #' non-functional sequence data sets. #' @param sequenceColumn name of the column containing IMGT-gapped sample sequences. #' @param germlineColumn name of the column containing IMGT-gapped germline sequences. #' @param vCallColumn name of the column containing the V-segment allele calls. #' @param multipleMutation string specifying how to handle multiple mutations occuring #' within the same 5-mer. If \code{"independent"} then multiple #' mutations within the same 5-mer are counted indepedently. #' If \code{"ignore"} then 5-mers with multiple mutations are #' excluded from the otal mutation tally. #' @param minNumMutations minimum number of mutations required to compute the 5-mer #' substitution rates. If the number of mutations for a 5-mer #' is below this threshold, its substitution rates will be #' estimated from neighboring 5-mers. Default is 50. #' @param minNumSeqMutations minimum number of mutations in sequences containing each 5-mer #' to compute the mutability rates. If the number is smaller #' than this threshold, the mutability for the 5-mer will be #' inferred. Default is 500. #' @param modelName name of the model. #' @param modelDescription description of the model and its source data. #' @param modelSpecies genus and species of the source sequencing data. #' @param modelDate date the model was built. If \code{NULL} the current date #' will be used. #' @param modelCitation publication source. #' #' @return A \link{TargetingModel} object. #' #' @details \strong{Caution: The targeting model functions do NOT support ambiguous #' characters in their inputs. You MUST make sure that your input and germline #' sequences do NOT contain ambiguous characters (especially if they are #' clonal consensuses returned from \code{collapseClones}).} #' #' @references #' \enumerate{ #' \item Yaari G, et al. Models of somatic hypermutation targeting and substitution based #' on synonymous mutations from high-throughput immunoglobulin sequencing data. #' Front Immunol. 2013 4(November):358. #' } #' #' @seealso See \link{TargetingModel} for the return object. #' See \link{plotMutability} plotting a mutability model. #' See \link{createSubstitutionMatrix}, \link{extendSubstitutionMatrix}, #' \link{createMutabilityMatrix}, \link{extendMutabilityMatrix} and #' \link{createTargetingMatrix} for component steps in building a model. #' #' @examples #' \donttest{ #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h")[1:80,] #' #' # Create model using only silent mutations and ignore multiple mutations #' model <- createTargetingModel(db, model="s", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", multipleMutation="ignore") #' #' # View top 5 mutability estimates #' head(sort(model@mutability, decreasing=TRUE), 5) #' #' # View number of silent mutations used for estimating mutability #' model@numMutS #' } #' #' @export createTargetingModel <- function(db, model=c("s", "rs"), sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call", multipleMutation=c("independent", "ignore"), minNumMutations=50, minNumSeqMutations=500, modelName="", modelDescription="", modelSpecies="", modelCitation="", modelDate=NULL) { # Evaluate argument choices model <- match.arg(model) multipleMutation <- match.arg(multipleMutation) # Check for valid columns check <- checkColumns(db, c(sequenceColumn, germlineColumn, vCallColumn)) if (check != TRUE) { stop(check) } # Check validity of input sequences # (MUST NOT CONTAIN AMBIGUOUS CHARACTERS -- not supported) bool_obsv <- checkAmbiguousExist(db[[sequenceColumn]]) bool_germ <- checkAmbiguousExist(db[[germlineColumn]]) if (any(bool_obsv | bool_germ)) { stop("Ambiguous characters are not supported in input sequences.") } # Set date if (is.null(modelDate)) { modelDate <- format(Sys.time(), "%Y-%m-%d") } # Create models sub_mat<- createSubstitutionMatrix(db, model=model, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, vCallColumn=vCallColumn, multipleMutation=multipleMutation, minNumMutations=minNumMutations, returnModel="5mer") mut_mat <- createMutabilityMatrix(db, sub_mat, model=model, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, vCallColumn=vCallColumn, multipleMutation=multipleMutation, minNumSeqMutations=minNumSeqMutations) # Extend 5-mers with Ns sub_mat <- extendSubstitutionMatrix(sub_mat) mut_mat <- extendMutabilityMatrix(mut_mat) # Make targeting matrix tar_mat <- createTargetingMatrix(sub_mat, mut_mat) # Define TargetingModel object model_obj <- new("TargetingModel", name=modelName, description=modelDescription, species=modelSpecies, date=modelDate, citation=modelCitation, substitution=sub_mat, mutability=mut_mat, targeting=tar_mat@.Data, numMutS=mut_mat@numMutS, numMutR=mut_mat@numMutR) return(model_obj) } #' Calculate total mutability #' #' \code{calculateMutability} calculates the total (summed) mutability for a set of sequences #' based on a 5-mer nucleotide mutability model. #' #' @param sequences character vector of sequences. #' @param model \link{TargetingModel} object with mutation likelihood information. #' @param progress if \code{TRUE} print a progress bar. #' #' @return Numeric vector with a total mutability score for each sequence. #' #' @examples #' \donttest{ #' # Subset example data to one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") #' #' # Calculate mutability of germline sequences using \link{HH_S5F} model #' mutability <- calculateMutability(sequences=db[["germline_alignment_d_mask"]], model=HH_S5F) #' } #' #' @export calculateMutability <- function(sequences, model=HH_S5F, progress=FALSE) { # Initialize variables alphb <- seqinr::s2c("ACGTN") model_kmer <- names(model@mutability) model_rates <- as.vector(model@mutability) sequences <- toupper(sequences) sequences <- gsub("\\.", "N", sequences) # Mutability calculation mutability <- vector(mode="numeric", length=length(sequences)) if (progress) { pb <- progressBar(length(sequences)) } for (s in 1:length(sequences)) { kmer <- seqinr::count(seqinr::s2c(sequences[s]), wordsize=5, alphabet=alphb) seq_kmer <- names(kmer) seq_counts <- as.vector(kmer) index <- match(seq_kmer, model_kmer) mutability[s] <- sum(seq_counts*model_rates[index], na.rm=TRUE) if (progress) { pb$tick() } } return(mutability) } # Create model and rescale mutabilities # model <- createTargetingModel(db, model="s", multipleMutation="ignore") # mut <- rescaleMutability(model) rescaleMutability <- function(model, mean=1.0) { if (is(model, "TargetingModel")) { model <- model@mutability } else if (!is(model, "vector")) { stop("Input must be either a mutability vector or TargetingModel object.") } # TODO: perhaps this is not so useful. or perhaps it should be relative to max(model). # Renormalize rescaled <- model / sum(model, na.rm=T) * sum(!is.na(model)) * mean rescaled[!is.finite(rescaled)] <- NA return(rescaled) } # Remove in-frame IMGT gaps # # @param matInput Nx2 matrix with input and germline sequences in each column # @return A two column matrix with "..." codons removed. removeCodonGaps <- function(matInput) { # Function to return valid codon sets # i = position, x = codon list 1, y = codon list 2 .f1 <- function(i, x, y) { xcod <- x[i] ycod <- y[i] if (xcod != "..." & ycod != "...") { c(xcod, ycod) } else { c("", "") } } # Function to parse sequences # z = vector of 2 sequences .f2 <- function(z) { # Split strings into 3-mers cods <- stri_extract_all_regex(c(z[1], z[2]), ".{3}") cmat <- sapply(1:length(cods[[1]]), .f1, x=cods[[1]], y=cods[[2]]) apply(cmat, 1, paste, collapse="") } matCollapsed <- t(apply(matInput, 1, .f2)) return(matCollapsed) } #' Make a degenerate 5-mer substitution model based on a 1-mer substitution model #' #' \code{makeDegenerate5merSub} populates substitution rates from a 1-mer substitution model #' into 5-mers with corresponding central 1-mers. #' #' @param sub1mer a 4x4 matrix containing (normalized) substitution rates. #' Row names should correspond to nucleotides to mutate from. #' Column names should correspond to nucleotides to mutate into. #' Nucleotides should include "A", "T", "G", and "C" #' (case-insensitive). #' @param extended whether to return the unextended (\code{extended=FALSE}) or #' extended (\code{extended=TRUE}) 5-mer substitution model. #' Default is \code{FALSE}. #' #' @return For \code{extended=FALSE}, a 4x1024 matrix. For \code{extended=TRUE}, a 5x3125 #' matrix. #' #' @details As a concrete example, consider a 1-mer substitution model in which substitution #' rates from "A" to "T", "G", and "C" are, respectively, 0.1, 0.6, and 0.3. In the #' resultant degenerate 5-mer substitution model, all the 5-mers (columns) that have #' an "A" as their central 1-mer would have substitution rates (rows) of 0.1, 0.6, and #' 0.3 to "T", "G", and "C" respectively. #' #' When \code{extended=TRUE}, \code{extendSubstitutionMatrix} is called to extend #' the 4x1024 substitution matrix. #' #' @seealso See \link{makeAverage1merSub} for making a 1-mer substitution model by taking #' the average of a 5-mer substitution model. See \link{extendSubstitutionMatrix} #' for extending the substitution matrix. #' #' @examples #' # Make a degenerate 5-mer model (4x1024) based on HKL_S1F (4x4) #' # Note: not to be confused with HKL_S5F@substitution, which is non-degenerate #' degenerate5merSub <- makeDegenerate5merSub(sub1mer = HKL_S1F) #' #' # Look at a few 5-mers #' degenerate5merSub[, c("AAAAT", "AACAT", "AAGAT", "AATAT")] #' #' @export makeDegenerate5merSub <- function(sub1mer, extended=FALSE) { # make sure that rownames and colnames of sub1mer are uppercase rownames(sub1mer) <- toupper(rownames(sub1mer)) colnames(sub1mer) <- toupper(colnames(sub1mer)) # create 5-mer labels using ATGC nuc_chars <- NUCLEOTIDES[1:4] nuc_words <- seqinr::words(5, nuc_chars) # get center positions of 5mers nuc_centers <- sapply(nuc_words, function(x){seqinr::s2c(x)[3]}) # initiate 5-mer substitution matrix (4x1024) sub5mer <- matrix(NA, nrow=4, ncol=length(nuc_words), dimnames=list(nuc_chars, nuc_words)) # assign values from 1-mer model to 5-mer model for (from in rownames(sub1mer)) { for (to in colnames(sub1mer)) { if (from != to) { # if statement keeps diagonals as NA colIndex <- which(nuc_centers == from) sub5mer[to, colIndex] <- sub1mer[from, to] } } } stopifnot(dim(sub5mer) == c(4, 1024)) # if extended=TRUE, extend if (extended) { sub5mer <- extendSubstitutionMatrix(sub5mer) stopifnot(dim(sub5mer) == c(5, 3125)) } return(sub5mer) } #' Make a degenerate 5-mer mutability model based on a 1-mer mutability model #' #' \code{makeDegenerate5merMut} populates mutability rates from a 1-mer mutability model #' into 5-mers with corresponding central 1-mers. #' #' @param mut1mer a named vector of length 4 containing (normalized) #' mutability rates. Names should correspond to nucleotides, #' which should include "A", "T", "G", and "C" #' (case-insensitive). #' @param extended whether to return the unextended (\code{extended=FALSE}) or #' extended (\code{extended=TRUE}) 5-mer mutability model. #' Default is \code{FALSE}. #' #' @return For \code{extended=FALSE}, a vector of length 1024. The vector returned is #' normalized. For \code{extended=TRUE}, a vector of length 3125. #' #' @details As a concrete example, consider a 1-mer mutability model in which mutability #' rates of "A", "T", "G", and "C" are, respectively, 0.14, 0.23, 0.31, and 0.32. #' In the resultant degenerate 5-mer mutability model, all the 5-mers that have #' an "A" as their central 1-mer would have mutability rate of 0.14/256, where 256 is #' the number of such 5-mers. #' #' When \code{extended=TRUE}, \code{extendMutabilityMatrix} is called to extend the #' mutability vector of length 1024 into a vector of length 3125. #' #' @seealso See \link{makeAverage1merMut} for making a 1-mer mutability model by #' taking the average of a 5-mer mutability model. See #' \link{extendMutabilityMatrix} for extending the mutability vector. #' #' @examples #' # Make a degenerate 5-mer model (length of 1024) based on a 1-mer model #' example1merMut <- c(A=0.2, T=0.1, C=0.4, G=0.3) #' degenerate5merMut <- makeDegenerate5merMut(mut1mer = example1merMut) #' #' # Look at a few 5-mers #' degenerate5merMut[c("AAAAT", "AACAT", "AAGAT", "AATAT")] #' #' # Normalized #' sum(degenerate5merMut) #' #' @export makeDegenerate5merMut <- function(mut1mer, extended=FALSE) { # make sure that names of mut1mer are uppercase names(mut1mer) <- toupper(names(mut1mer)) # create 5-mer labels using ATGCN nuc_chars <- NUCLEOTIDES[1:4] nuc_words <- seqinr::words(5, nuc_chars) # get center positions of 5mers nuc_centers <- sapply(nuc_words, function(x){seqinr::s2c(x)[3]}) # initiate 5-mer mutability vector (length of 3125) mut5mer <- rep(NA, length=length(nuc_words)) names(mut5mer) <- nuc_words # assign values from 1-mer model to 5-mer model for (center in names(mut1mer)) { index <- which(nuc_centers == center) mut5mer[index] <- mut1mer[center] } stopifnot(length(mut5mer) == 1024) # normalize mut5mer <- mut5mer / sum(mut5mer, na.rm=T) # if extended=TRUE, extend if (extended) { mut5mer <- extendMutabilityMatrix(mut5mer) stopifnot(length(mut5mer) == 3125) } return(mut5mer) } #' Make a 1-mer substitution model by averaging over a 5-mer substitution model #' #' \code{makeAverage1merSub} averages substitution rates in a 5-mer substitution model #' to derive a 1-mer substitution model. #' #' @param sub5mer a 4x1024 matrix such as that returned by #' \code{createSubstitutionMatrix} and that returned by #' \code{makeDegenerate5merSub} with \code{extended=FALSE}. #' Column names should correspond to 5-mers containing the #' central 1-mer to mutate from. Row names should correspond to #' nucleotides to mutate into. Nucleotides should include #' "A", "T", "G", and "C" (case-insensitive). #' #' @return A 4x4 matrix with row names representing nucleotides to mutate from and column #' names representing nucleotides to mutate into. Rates are normalized by row. #' #' @details For example, the substitution rate from "A" to "T" in the resultant 1-mer model #' is derived by averaging the substitution rates into a "T" of all the 5-mers that #' have an "A" as their central 1-mer. #' #' @seealso See \link{makeDegenerate5merSub} for making a degenerate 5-mer substitution #' model based on a 1-mer substitution model. #' #' @examples #' # Make a degenerate 5-mer model (4x1024) based on HKL_S1F (4x4) #' degenerate5merSub <- makeDegenerate5merSub(sub1mer = HKL_S1F) #' #' # Now make a 1-mer model by averaging over the degenerate 5-mer model #' # Expected to get back HKL_S1F #' makeAverage1merSub(sub5mer = degenerate5merSub) #' #' @export makeAverage1merSub <- function(sub5mer) { stopifnot(dim(sub5mer) == c(4, 1024)) # make sure that rownames and colnames of sub5mer are uppercase rownames(sub5mer) <- toupper(rownames(sub5mer)) colnames(sub5mer) <- toupper(colnames(sub5mer)) # get 5-mers and center positions of 5-mers nuc_words <- colnames(sub5mer) nuc_centers <- sapply(nuc_words, function(x){seqinr::s2c(x)[3]}) # create 1-mer labels using ATGC nuc_chars <- NUCLEOTIDES[1:4] # initiate 1-mer substitution matrix (4x4) sub1mer <- matrix(NA, nrow=length(nuc_chars), ncol=length(nuc_chars), dimnames=list(nuc_chars, nuc_chars)) # assign values from 5-mer model to 1-mer model for (from in rownames(sub1mer)) { for (to in colnames(sub1mer)) { if (from != to) { # if statement keeps diagonals as NA colIndex <- which(nuc_centers == from) sub1mer[from, to] <- mean(sub5mer[to, colIndex], na.rm=T) } } } stopifnot(dim(sub1mer) == c(4, 4)) # normalize # tricky: apply transposes result; use t() to transpose back sub1mer <- t(apply(sub1mer, 1, function(x){x/sum(x, na.rm=T)})) return(sub1mer) } #' Make a 1-mer mutability model by averaging over a 5-mer mutability model #' #' \code{makeAverage1merMut} averages mutability rates in a 5-mer mutability model #' to derive a 1-mer mutability model. #' #' @param mut5mer a named vector of length 1024 such as that returned by #' \code{createMutabilityMatrix} and that returned by #' \code{makeDegenerate5merMut} with \code{extended=FALSE}. #' Names should correspond to 5-mers made up of "A", "T", #' "G", and "C" (case-insensitive). \code{NA} values are #' allowed. #' #' @return A named vector of length 4 containing normalized mutability rates. #' #' @details For example, the mutability rate of "A" in the resultant 1-mer model #' is derived by averaging the mutability rates of all the 5-mers that #' have an "A" as their central 1-mer, followed by normalization. #' #' @seealso See \link{makeDegenerate5merMut} for making a degenerate 5-mer mutability #' model based on a 1-mer mutability model. #' #' @examples #' # Make a degenerate 5-mer model (length of 1024) based on a 1-mer model #' example1merMut <- c(A=0.2, T=0.1, C=0.4, G=0.3) #' degenerate5merMut <- makeDegenerate5merMut(mut1mer = example1merMut) #' #' # Now make a 1-mer model by averaging over the degenerate 5-mer model #' # Expected to get back example1merMut #' makeAverage1merMut(mut5mer = degenerate5merMut) #' #' @export makeAverage1merMut <- function(mut5mer) { stopifnot(length(mut5mer) == 1024) # make sure that names mut5mer are uppercase names(mut5mer) <- toupper(names(mut5mer)) # get 5-mers and center positions of 5-mers nuc_words <- names(mut5mer) nuc_centers <- sapply(nuc_words, function(x){seqinr::s2c(x)[3]}) # create 1-mer labels using ATGC nuc_chars <- NUCLEOTIDES[1:4] # initiate 1-mer mutability vector (length 4) mut1mer <- rep(NA, length=length(nuc_chars)) names(mut1mer) <- nuc_chars # assign values from 5-mer model to 1-mer model for (center in names(mut1mer)) { index <- which(nuc_centers == center) mut1mer[center] <- mean(mut5mer[index], na.rm=T) } stopifnot(length(mut1mer) == 4) # normalize mut1mer <- mut1mer / sum(mut1mer, na.rm=T) return(mut1mer) } #### Distance Functions #### #' Calculates a 5-mer distance matrix from a TargetingModel object #' #' \code{calcTargetingDistance} converts either the targeting rates in a \code{TargetingModel} #' model to a matrix of 5-mer to single-nucleotide mutation distances, or the substitution #' rates in a 1-mer substitution model to a symmetric distance matrix. #' #' @param model \link{TargetingModel} object with mutation likelihood information, or #' a 4x4 1-mer substitution matrix normalized by row with rownames and #' colnames consisting of "A", "T", "G", and "C". #' @param places decimal places to round distances to. #' #' @return For input of \link{TargetingModel}, a matrix of distances for each 5-mer motif with #' rows names defining the center nucleotide and column names defining the 5-mer #' nucleotide sequence. For input of 1-mer substitution matrix, a 4x4 symmetric distance #' matrix. #' #' @details #' The targeting model is transformed into a distance matrix by: #' \enumerate{ #' \item Converting the likelihood of being mutated \eqn{p=mutability*substitution} to #' distance \eqn{d=-log10(p)}. #' \item Dividing this distance by the mean of the distances. #' \item Converting all infinite, no change (e.g., A->A), and NA distances to #' zero. #' } #' #' The 1-mer substitution matrix is transformed into a distance matrix by: #' \enumerate{ #' \item Symmetrize the 1-mer substitution matrix. #' \item Converting the rates to distance \eqn{d=-log10(p)}. #' \item Dividing this distance by the mean of the distances. #' \item Converting all infinite, no change (e.g., A -> A), and NA distances to #' zero. #' } #' #' @seealso See \link{TargetingModel} for this class of objects and #' \link{createTargetingModel} for building one. #' #' @examples #' # Calculate targeting distance of HH_S5F #' dist <- calcTargetingDistance(HH_S5F) #' #' # Calculate targeting distance of HH_S1F #' dist <- calcTargetingDistance(HH_S1F) #' #' @export calcTargetingDistance <- function(model, places=2) { # if model is TargetingModel object, assume 5-mer targeting model # extract targeting matrix if (is(model, "TargetingModel")) { input <- "5mer" model <- model@targeting } else if (is(model, "matrix") & all(dim(model) == c(4, 4))) { # if model is a matrix, assume 1-mer substitution matrix and symmetrize input <- "1mer" model <- symmetrize(model) } else { # anything else: error stop("Input must be either a 4x4 targeting matrix or TargetingModel object.") } # Take negative log10 of all probabilities model_dist <- -log10(model) # Center distances on 1 (divide by mean) model_dist <- model_dist/mean(model_dist, na.rm=T) # Set infinite values to NA model_dist[!is.finite(model_dist)] <- NA # TODO: the indexing of A-->A etc positions can probably be done smarter/faster # Assign no-change entries to distance 0 if (input == "5mer") { center_nuc <- gsub("..([ACGTN])..", "\\1", colnames(model_dist)) for (i in 1:length(center_nuc)) { model_dist[center_nuc[i], i] <- 0 } # Assign 0 to N and 5mers with N in center position model_dist[,center_nuc=="N"] <- 0 model_dist["N",] <- 0 } else if (input == "1mer") { diag(model_dist) <- 0 model_dist <- rbind(model_dist, matrix(0, 3, 4)) model_dist <- cbind(model_dist, matrix(0, 7, 3)) colnames(model_dist)[5:7] <- rownames(model_dist)[5:7] <- c("N", "-", ".") } # Round model_dist <- round(model_dist, places) return(model_dist) } # (From G Yaari) # Symmetrize a non-symmetric, 1-mer substitution matrix # # \code{symmetrize} makes a 1-mer substitution matrix symmetric by minimizing the # rss between it and a symmetric matrix. # # @param sub1mer a 4x4 matrix normalized by row. Each row sums up to 1 and # reflects substitution probabilities for each nucleotide. # Rownames and colnames are "A","C","G", and "T". # # @return a 4x4 symmetrix matrix with \code{NA}s on the diagonal. # # @details The input 1-mer substitution matrix is approximated by a symmetric matrix # both with respect to time (e.g. C->T = T->C), and with respect to strand # (e.g. C->T = G->A). The symmetric matrix has three free parameters that # are estimated by minimizing the sum of squares between this matrix and # the input matrix. The fitted matrix was normalized to ensure that each # row sums up to 1. symmetrize <- function(sub1mer) { rownames(sub1mer) <- toupper(rownames(sub1mer)) colnames(sub1mer) <- toupper(colnames(sub1mer)) # check that rows sum up to 1 stopifnot(all.equal(rowSums(sub1mer), rep(1, 4), check.names=FALSE, tolerance=0.055)) .minDist <- function(Pars, subMtx) { (Pars[1] - subMtx["A", "C"])^2 + (Pars[1] - subMtx["C", "A"])^2 + (Pars[1] - subMtx["G", "T"])^2 + (Pars[1] - subMtx["T", "G"])^2 + (Pars[2] - subMtx["A", "G"])^2 + (Pars[2] - subMtx["G", "A"])^2 + (Pars[2] - subMtx["C", "T"])^2 + (Pars[2] - subMtx["T", "C"])^2 + (Pars[3] - subMtx["A", "T"])^2 + (Pars[3] - subMtx["T", "A"])^2 + (Pars[3] - subMtx["C", "G"])^2 + (Pars[3] - subMtx["G", "C"])^2 } pars <- optim(par=rep(0, 3), fn=.minDist, subMtx=sub1mer)$par pars <- pars/sum(pars) symmetric_substitution <- sub1mer symmetric_substitution["A", 2:4] <- pars symmetric_substitution["C", c(1, 4, 3)] <- pars symmetric_substitution["G", c(4, 1, 2)] <- pars symmetric_substitution["T", c(3, 2, 1)] <- pars # NAs on diagonal instead of 0 so that calcTargetingDistance works with 1-mer model diag(symmetric_substitution) <- NA return(symmetric_substitution) } #' Write targeting model distances to a file #' #' \code{writeTargetingDistance} writes a 5-mer targeting distance matrix #' to a tab-delimited file. #' #' @param model \link{TargetingModel} object with #' mutation likelihood information. #' @param file name of file to write. #' #' @return NULL #' #' @details #' The targeting distance write as a tab-delimited 5x3125 matrix. Rows define the mutated #' nucleotide at the center of each 5-mer, one of \code{c("A", "C", "G", "T", "N")}, #' and columns define the complete 5-mer of the unmutated nucleotide sequence. #' \code{NA} values in the distance matrix are replaced with distance 0. #' #' @seealso Takes as input a \link{TargetingModel} object and calculates #' distances using \link{calcTargetingDistance}. #' #' @examples #' \dontrun{ #' # Write HS5F targeting model to working directory as hs5f.tab #' writeTargetingDistance(HH_S5F, "hh_s5f.tsv") #' } #' #' @export writeTargetingDistance <- function(model, file) { to_write <- as.data.frame(calcTargetingDistance(model)) to_write[is.na(to_write)] <- 0 write.table(to_write, file, quote=FALSE, sep="\t", col.names=NA, row.names=TRUE) } #### Plotting functions #### #' Plot mutability probabilities #' #' \code{plotMutability} plots the mutability rates of a \code{TargetingModel}. #' #' @param model \link{TargetingModel} object or vector containing normalized #' mutability rates. #' @param nucleotides vector of center nucleotide characters to plot. #' @param mark vector of 5-mer motifs to highlight in the plot. If \code{NULL} #' only highlight classical hot and cold spot motifs. #' @param style type of plot to draw. One of: #' \itemize{ #' \item \code{"hedgehog"}: circular plot showing higher mutability #' scores further from the circle. The 5-mer #' is denoted by the values of the inner #' circle. The 5-mer is read from the most interior #' position of the 5-mer (5') to most exterior position #' (3'), with the center nucleotide in the center ring. #' Note, the order in which the 5-mers are plotted is #' different for nucleotides \code{c("A", "C")} and #' \code{c("G", "T")}. #' \item \code{"bar"}: bar plot of mutability similar to the #' \code{hedgehog} style with the most 5' positions #' of each 5-mer at the base of the plot. #' } #' @param size numeric scaling factor for lines and text in the plot. #' @param silent if \code{TRUE} do not draw the plot and just return the ggplot2 #' objects; if \code{FALSE} draw the plot. #' @param ... additional arguments to pass to ggplot2::theme. #' #' @return A named list of ggplot objects defining the plots, with names defined by the #' center nucleotide for the plot object. #' #' @seealso Takes as input a \link{TargetingModel} object. #' See \link{createTargetingModel} for model building. #' #' #' @examples #' # Plot one nucleotide in circular style #' plotMutability(HH_S5F, "C") #' #' # Plot two nucleotides in barchart style #' plotMutability(HH_S5F, c("G", "T"), style="bar") #' #' @export plotMutability <- function(model, nucleotides=c("A", "C", "G", "T"), mark=NULL, style=c("hedgehog", "bar"), size=1, silent=FALSE, ...) { # model=HH_S5F # nucleotides=c("C") # nucleotides=c("A", "C", "G", "T") # style="hedgehog" # size=1 # silent=FALSE # Check input nucleotides <- toupper(nucleotides) style <- match.arg(style) if (is(model, "TargetingModel")) { model <- model@mutability } else if (!is(model, "vector")) { stop("Input must be either a mutability vector or TargetingModel object.") } # Set base plot settings base_theme <- theme_bw() + theme(panel.spacing=grid::unit(0, "lines"), panel.background=element_blank()) + theme(axis.text=element_text(margin=grid::unit(0, "lines"))) + theme(text=element_text(size=10*size), title=element_text(size=10*size), legend.spacing=grid::unit(0, "lines"), legend.background=element_blank()) # Scaling and layout parameters score_offset <- 0 score_scale <- 15 text_offset <- -5.6 # Set guide colors motif_colors <- setNames(c("#000000", "#4daf4a", "#e41a1c", "#094d85", "#999999"), c("Marked", "WA/TW", "WRC/GYW", "SYC/GRS", "Neutral")) dna_colors <- setNames(c("#7bce77", "#ff9b39", "#f04949", "#5796ca", "#c4c4c4"), c("A", "C", "G", "T", "N")) # Build data.frame of mutability scores mut_scores <- model[!grepl("N", names(model))] mut_scores[!is.finite(mut_scores)] <- 0 mut_words <- names(mut_scores) mut_positions <- as.data.frame(t(sapply(mut_words, seqinr::s2c))) colnames(mut_positions) <- paste0("pos", 1:ncol(mut_positions)) mut_df <- data.frame(word=mut_words, score=mut_scores, mut_positions) # Add hot and cold-spot motif information mut_df$motif <- "Neutral" mut_df$motif[grepl("(.[AT]A..)|(..T[AT].)", mut_df$word, perl=TRUE)] <- "WA/TW" mut_df$motif[grepl("([AT][GA]C..)|(..G[CT][AT])", mut_df$word, perl=TRUE)] <- "WRC/GYW" mut_df$motif[grepl("([CG][CT]C..)|(..G[GA][CG])", mut_df$word, perl=TRUE)] <- "SYC/GRS" if (is.null(mark)) { mut_df$motif <- factor(mut_df$motif, levels=c("WA/TW", "WRC/GYW", "SYC/GRS", "Neutral")) } else { mut_df$motif[mut_df$word %in% mark] <- "Marked" mut_df$motif <- factor(mut_df$motif, levels=c("Marked", "WA/TW", "WRC/GYW", "SYC/GRS", "Neutral")) } # Subset to nucleotides of interest mut_df <- mut_df[mut_df$pos3 %in% nucleotides, ] # Functions to transform and revert score for plotting score_max <- max(mut_df$score, na.rm=TRUE) .transform_score <- function(x) { x / score_max * score_scale + score_offset } .invert_score <- function(x) { (x - score_offset) / score_scale * score_max } # Rescale scores for plotting mut_df$score <- .transform_score(mut_df$score) # Build plots for each center nucleotide plot_list <- list() for (center_nuc in nucleotides) { # center_nuc <- "C" # Subset data to center nucleotide sub_df <- mut_df[mut_df$pos3 == center_nuc, ] # Order 5-mers by positions, with reversed order if center nucleotide is G or T if (center_nuc %in% c("A", "C")) { sub_df <- dplyr::arrange(sub_df, !!!rlang::syms(c("pos1", "pos2", "pos4", "pos5"))) sub_df$x <- 1:nrow(sub_df) } else if (center_nuc %in% c("G", "T")) { sub_df <- dplyr::arrange(sub_df, !!!rlang::syms(c("pos5", "pos4", "pos2", "pos1"))) sub_df$x <- 1:nrow(sub_df) } else { stop("Invalid nucleotide choice") } # Melt 5-mer position data sub_melt <- sub_df %>% tidyr::gather("pos", "char", !!!rlang::syms(colnames(mut_positions))) %>% select("x", "pos", "char") #sub_melt$pos <- factor(sub_melt$pos, levels=mut_names) #sub_melt$pos <- as.numeric(sub_melt$pos) sub_melt$pos <- as.numeric(gsub("pos", "", sub_melt$pos)) # Define nucleotide text and rectangle position data sub_text <- list() for (i in 1:5) { nuc_rle <- rle(sub_melt$char[sub_melt$pos == i]) # Set rectangle x limits rect_max <- cumsum(nuc_rle$lengths) rect_min <- rect_max - diff(c(0, rect_max)) # Set text position if (length(rect_max) > 1) { text_x <- rect_max - diff(c(0, rect_max)) / 2 } else { text_x <- rect_max / 2 } tmp_df <- data.frame(text_x=text_x, text_y=i, text_label=factor(nuc_rle$values, levels=names(dna_colors)), rect_min=rect_min, rect_max=rect_max) sub_text[[i]] <- tmp_df } # Define text and rectangle positions for inner circle sub_melt$pos <- sub_melt$pos + text_offset sub_text <- lapply(sub_text, function(x) { dplyr::mutate(x, text_y=!!rlang::sym("text_y") + !!rlang::sym("text_offset")) }) sub_rect <- dplyr::bind_rows(sub_text) %>% mutate(rect_width=rect_max - rect_min, ymin=!!rlang::sym("text_y") - 0.5, ymax=!!rlang::sym("text_y") + 0.5) # Use only colors for motifs present in sub_df sub_colors <- motif_colors[names(motif_colors) %in% sub_df$motif] # Define base plot object p1 <- ggplot(sub_df) + base_theme + #ggtitle(paste0("NN", center_nuc, "NN")) + xlab("") + ylab("") + scale_color_manual(name="Motif", values=c(sub_colors, dna_colors), breaks=names(sub_colors)) + scale_fill_manual(name="", values=c(sub_colors, dna_colors), guide="none") + geom_rect(data=sub_rect, mapping=aes(xmin=!!rlang::sym("rect_min"), xmax=!!rlang::sym("rect_max"), ymin=!!rlang::sym("ymin"), ymax=!!rlang::sym("ymax"), fill=!!rlang::sym("text_label"), color=!!rlang::sym("text_label")), size=0.5*size, alpha=1, show.legend=FALSE) + #geom_tile(data=sub_rect, # mapping=aes_string(x="text_x", y="text_y", width="rect_width", fill="text_label"), # size=0, alpha=0.7, show.legend=FALSE) + #geom_tile(data=sub_melt, mapping=aes_string(x="x", y="pos", fill="char"), size=0, alpha=0.7, # show.legend=FALSE) + geom_text(data=sub_text[[3]], mapping=aes(x=!!rlang::sym("text_x"), y=!!rlang::sym("text_y"), label=!!rlang::sym("text_label")), color="black", hjust=0.5, vjust=0.5, size=3*size, fontface=2) # Add 5-mer nucleotide labels if (center_nuc %in% c("A", "C")) { p1 <- p1 + geom_text(data=sub_text[[1]], mapping=aes(x=!!rlang::sym("text_x"), y=!!rlang::sym("text_y"), label=!!rlang::sym("text_label")), color="black", hjust=0.5, vjust=0.5, size=2*size) + geom_text(data=sub_text[[2]], mapping=aes(x=!!rlang::sym("text_x"), y=!!rlang::sym("text_y"), label=!!rlang::sym("text_label")), color="black", hjust=0.5, vjust=0.5, size=2*size) } else if (center_nuc %in% c("G", "T")) { p1 <- p1 + geom_text(data=sub_text[[4]], mapping=aes(x=!!rlang::sym("text_x"), y=!!rlang::sym("text_y"), label=!!rlang::sym("text_label")), color="black", hjust=0.5, vjust=0.5, size=2*size) + geom_text(data=sub_text[[5]], mapping=aes(x=!!rlang::sym("text_x"), y=!!rlang::sym("text_y"), label=!!rlang::sym("text_label")), color="black", hjust=0.5, vjust=0.5, size=2*size) } # Add style options and mutability scores if (style == "hedgehog") { y_limits <- c(text_offset - 1, score_scale + score_offset) #orient_x <- sub_text[[3]]$text_x[1] #orient_y <- text_offset - 1 p1 <- p1 + theme(plot.margin=grid::unit(c(0, 0, 0, 0), "lines"), panel.grid=element_blank(), panel.border=element_blank(), axis.title=element_blank(), axis.text=element_blank(), axis.ticks=element_blank(), legend.direction="horizontal", legend.justification=c(0.5, 1), legend.position=c(0.5, 1)) + scale_x_continuous(expand=c(0, 0)) + scale_y_continuous(limits=y_limits, expand=c(0, 0)) + coord_polar(theta="x") + geom_segment(data=sub_df, mapping=aes(x=!!rlang::sym("x"), xend=!!rlang::sym("x"), yend=!!rlang::sym("score"), color=!!rlang::sym("motif")), y=score_offset, size=0.75*size, position=position_nudge(x = -0.5)) + guides(color=guide_legend(override.aes=list(linetype=1, size=2*size))) } else if (style == "bar") { y_breaks <- seq(score_offset, score_scale + score_offset, 1) y_limits <- c(text_offset + 0.5, score_scale + score_offset) p1 <- p1 + theme(plot.margin=grid::unit(c(1, 1, 1, 1), "lines"), panel.grid=element_blank(), panel.border=element_rect(color="black"), axis.text.x=element_blank(), axis.ticks.x=element_blank(), legend.position="top") + ylab("Mutability") + scale_x_continuous(expand=c(0, 1)) + scale_y_continuous(limits=y_limits, breaks=y_breaks, expand=c(0, 0.5), labels=function(x) scales::scientific(.invert_score(x))) + geom_bar(data=sub_df, mapping=aes(x=!!rlang::sym("x"), y=!!rlang::sym("score"), fill=!!rlang::sym("motif"), color=!!rlang::sym("motif")), stat="identity", position=position_nudge(x = -0.5), size=0, width=0.7) + guides(color=guide_legend(override.aes=list(fill=sub_colors, linetype=0))) } # Add additional theme elements p1 <- p1 + do.call(theme, list(...)) # Add plot to list plot_list[[center_nuc]] <- p1 } # Plot if (!silent) { do.call(gridPlot, args=c(plot_list, ncol=length(plot_list))) } invisible(plot_list) } #' Visualize parameter tuning for minNumMutations and minNumSeqMutations #' #' Visualize results from \link{minNumMutationsTune} and \link{minNumSeqMutationsTune} #' #' @param tuneMtx a \code{matrix} or a \code{list} of matrices produced by either #' \link{minNumMutationsTune} or \link{minNumSeqMutationsTune}. #' In the case of a list, it is assumed that each matrix corresponds #' to a sample and that all matrices in the list were produced using #' the same set of trial values of \code{minNumMutations} or #' \code{minNumSeqMutations}. #' @param thresh a number or a vector of indicating the value or the range of values #' of \code{minNumMutations} or \code{minNumSeqMutations} to plot. #' Should correspond to the columns of \code{tuneMtx}. #' @param criterion one of \code{"5mer"}, \code{"3mer"}, \code{"1mer"}, or \code{"3mer+1mer"} #' (for \code{tuneMtx} produced by \link{minNumMutationsTune}), or either #' \code{"measured"} or \code{"inferred"} (for \code{tuneMtx} produced by #' \link{minNumSeqMutationsTune}). #' @param pchs point types to pass on to \link{plot}. #' @param ltys line types to pass on to \link{plot}. #' @param cols colors to pass on to \link{plot}. #' @param plotLegend whether to plot legend. Default is \code{TRUE}. Only applicable #' if \code{tuneMtx} is a named list with names of the matrices #' corresponding to the names of the samples. #' @param legendPos position of legend to pass on to \link{legend}. Can be either a #' numeric vector specifying x-y coordinates, or one of #' \code{"topright"}, \code{"center"}, etc. Default is \code{"topright"}. #' @param legendHoriz whether to make legend horizontal. Default is \code{FALSE}. #' @param legendCex numeric values by which legend should be magnified relative to 1. #' #' @details For \code{tuneMtx} produced by \link{minNumMutationsTune}, for each sample, depending on #' \code{criterion}, the numbers of 5-mers for which substitution rates are directly computed #' (\code{"5mer"}), inferred based on inner 3-mers (\code{"3mer"}), inferred based on #' central 1-mers (\code{"1mer"}), or inferred based on inner 3-mers and central 1-mers #' (\code{"3mer+1mer"}) are plotted on the y-axis against values of \code{minNumMutations} #' on the x-axis. #' #' For \code{tuneMtx} produced by \link{minNumSeqMutationsTune}, for each sample, depending on #' \code{criterion}, the numbers of 5-mers for which mutability rates are directly measured #' (\code{"measured"}) or inferred (\code{"inferred"}) are plotted on the y-axis against values #' of \code{minNumSeqMutations} on the x-axis. #' #' Note that legends will be plotted only if \code{tuneMtx} is a supplied as a named \code{list} #' of matrices, ideally with names of each \code{matrix} corresponding to those of the samples #' based on which the matrices were produced, even if \code{plotLegend=TRUE}. #' #' @seealso See \link{minNumMutationsTune} and \link{minNumSeqMutationsTune} for generating #' \code{tuneMtx}. #' #' @examples #' \donttest{ #' # Subset example data to one isotype and 200 sequences #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA") #' set.seed(112) #' db <- dplyr::slice_sample(db, n=50) #' #' tuneMtx = list() #' for (i in 1:length(unique(db$sample_id))) { #' # Get data corresponding to current sample #' curDb = db[db[["sample_id"]] == unique(db[["sample_id"]])[i], ] #' #' # Count the number of mutations per 5-mer #' subCount = createSubstitutionMatrix(db=curDb, model="s", #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' vCallColumn="v_call", #' multipleMutation="independent", #' returnModel="5mer", numMutationsOnly=TRUE) #' #' # Tune over minNumMutations = 5..50 #' subTune = minNumMutationsTune(subCount, seq(from=5, to=50, by=5)) #' #' tuneMtx = c(tuneMtx, list(subTune)) #' } #' #' # Name tuneMtx after sample names #' names(tuneMtx) = unique(db[["sample_id"]]) #' #' # plot with legend for both samples for a subset of minNumMutations values #' plotTune(tuneMtx, thresh=c(5, 15, 25, 40), criterion="3mer", #' pchs=16:17, ltys=1:2, cols=2:3, #' plotLegend=TRUE, legendPos=c(25, 30)) #' #' # plot for only 1 sample for all the minNumMutations values (no legend) #' plotTune(tuneMtx[[1]], thresh=seq(from=5, to=50, by=5), criterion="3mer") #' } #' #' @export plotTune <- function(tuneMtx, thresh, criterion=c("5mer", "3mer", "1mer", "3mer+1mer", "measured", "inferred"), pchs = 1, ltys = 2, cols = 1, plotLegend = TRUE, legendPos = "topright", legendHoriz = FALSE, legendCex = 1) { stopifnot(length(criterion)==1) stopifnot(is.matrix(tuneMtx) | is.list(tuneMtx)) ### extract plot data into plotMtx # if tuneMtx is just a matrix if (!is.list(tuneMtx)) { if (criterion!="3mer+1mer") { plotMtx <- matrix(tuneMtx[criterion, as.character(thresh)], nrow=1) } else { plotMtx <- matrix(colSums(tuneMtx[c("3mer", "1mer"), as.character(thresh)]), nrow=1) } } else { # if tuneMtx is a named list of matrices (e.g. corresponding to multiple samples) if (criterion!="3mer+1mer") { plotMtx <- do.call(base::rbind, lapply(tuneMtx, function(mtx){mtx[criterion, as.character(thresh)]})) } else { plotMtx <- do.call(base::rbind, lapply(tuneMtx, function(mtx){colSums(mtx[c("3mer", "1mer"), as.character(thresh)])})) } rownames(plotMtx) <- names(tuneMtx) } # sanity check: there should not be any NA stopifnot(!any(is.na(plotMtx))) ### if number of pchs/ltys/cols provided does not match number of samples expected # expand into vector with repeating values (otherwise legend would break) if (length(pchs)!=nrow(plotMtx)) {pchs <- rep(pchs, length.out=nrow(plotMtx))} if (length(ltys)!=nrow(plotMtx)) {ltys <- rep(ltys, length.out=nrow(plotMtx))} if (length(cols)!=nrow(plotMtx)) {cols <- rep(cols, length.out=nrow(plotMtx))} ### axis labels if (criterion %in% c("5mer", "3mer", "1mer", "3mer+1mer")) { xlab.name <- "Minimum # mutations per 5-mer to\ndirectly compute 5-mer substitution rates" # cannot use switch because variable names cannot start with number ylab.name <- "# 5-mers for which substitution rates are\n" if (criterion=="5mer") { ylab.name <- paste(ylab.name, "directly computed") } else if (criterion=="3mer") { ylab.name <- paste(ylab.name, "inferred based on inner 3-mers") } else if (criterion=="1mer") { ylab.name <- paste(ylab.name, "inferred based on central 1-mers") } else if (criterion=="3mer+1mer") { ylab.name <- paste(ylab.name, "inferred based on 3- and 1-mers") } } else if (criterion %in% c("measured", "inferred")) { xlab.name <- "Minimum # mutations in sequences containing each 5-mer\nto directly compute mutability" ylab.name <- paste("# 5-mers for which mutability is", criterion) } ### plot # bottom, left, top, right par(mar=c(6, 6, 4, 2) + 0.1) for (i in 1:nrow(plotMtx)) { if (i==1) { plot(x=thresh, y=plotMtx[i, ], ylim=range(plotMtx), xaxt="n", xlab="", ylab="", cex.axis=1.5, type="b", lwd=1.5, pch=pchs[i], lty=ltys[i], col=cols[i]) axis(side=1, at=thresh, cex.axis=1.5) mtext(side=1, text=xlab.name, line=4, cex=1.2) mtext(side=2, text=ylab.name, line=3, cex=1.2) } else { points(x=thresh, y=plotMtx[i, ], type="b", lwd=1.5, pch=pchs[i], lty=ltys[i], col=cols[i]) } } ### legend (even if plotLegend=T, only plotted if tuneMtx is a named list) if ( !is.null(rownames(plotMtx)) & plotLegend ) { # if legendPos specified as xy coordinates if (is.numeric(legendPos) & length(legendPos)==2) { legend(x=legendPos[1], y=legendPos[2], legend = c("Sample", rownames(plotMtx)), horiz = legendHoriz, cex = legendCex, pch=c(NA, pchs), lty=c(NA, ltys), col=c(NA, cols)) } else { # if legendPos specified as "center", "topright", etc. legend(legendPos, legend = c("Sample", rownames(plotMtx)), horiz = legendHoriz, cex = legendCex, pch=c(NA, pchs), lty=c(NA, ltys), col=c(NA, cols)) } } } #### Original BASELINe functions #### # Given a nuc, returns the other 3 nucs it can mutate to canMutateTo <- function(nuc) { NUCLEOTIDES[1:4][NUCLEOTIDES[1:4] != nuc] } # Compute the mutations types # matOfCodons: nx2; n=pairs of codons; 1st col=codonTo, 2nd col=codonFrom # NOTE: this function is not intended to be used where input sequences have # ambiguous characters; it assumes that only 1 entry (r/s/stop/na) from # mutationType is non-zero/1 mutationTypeOptimized <- function(matOfCodons) { # mutType: 4xn; rows: r/s/stop/na mutType <- apply(matOfCodons, 1, function(x) { mutationType(x[2], x[1]) }) idx <- apply(mutType, 2, function(y){which(y>0)[1]}) mutType <- rownames(mutType)[idx] mutType[which(mutType=="na")] <- NA return(mutType) } # row 1 = GL # row 2 = Seq # in_matrix <- matrix(c(c("A","A","A","C","C","C"), c("A","G","G","C","C","A")), 2 ,6, byrow=T) # analyzeMutations2NucUri(in_matrix) analyzeMutations2NucUri <- function(in_matrix) { if(ncol(in_matrix) > VLENGTH) { paramGL <- in_matrix[2,1:VLENGTH] paramSeq <- in_matrix[1,1:VLENGTH] } else { paramGL <- in_matrix[2,] paramSeq <- in_matrix[1,] } #mutations = apply(rbind(paramGL,paramSeq), 2, function(x){!x[1]==x[2]}) mutations_val <- paramGL != paramSeq if (any(mutations_val)) { mutationPos <- {1:length(mutations_val)}[mutations_val] #mutationPos = mutationPos[sapply(mutationPos, function(x){!any(paramSeq[getCodonPos(x)]=="N")})] length_mutations =length(mutationPos) mutationInfo <- rep(NA,length_mutations) if (any(mutationPos)) { pos<- mutationPos pos <- pos[!is.na(pos)] pos_array <- array(sapply(pos,getCodonPos)) codonGL <- paramGL[pos_array] codonGL[is.na(codonGL)] <- "N" codonSeq <- sapply(pos,function(x){ seqP <- paramGL[getCodonPos(x)] muCodonPos <- {x-1}%%3+1 seqP[muCodonPos] <- paramSeq[x] return(seqP) }) codonSeq[is.na(codonSeq)] <- "N" GLcodons <- apply(matrix(codonGL,length_mutations,3,byrow=TRUE),1,c2s) Seqcodons <- apply(codonSeq,2,c2s) mutationInfo <- apply(rbind(GLcodons , Seqcodons),2,function(x){ # not intended to be used where input sequences have # ambiguous characters; it assumes that only 1 entry (r/s/stop/na) from # mutationType is non-zero/1 mutType <- mutationType(c2s(x[1]),c2s(x[2])) mutType <- names(mutType)[which(mutType>0)] if (mutType=="na") {mutType=NA} return(mutType) }) names(mutationInfo) <- mutationPos } if (any(!is.na(mutationInfo))) { return(mutationInfo[!is.na(mutationInfo)]) } else { return(NA) } } else { return (NA) } } # List mutations listMutations <- function(seqInput, seqGL, multipleMutation, model) { #if( is.na(c(seqInput, seqGL)) ) return(array(NA,4)) if (is.na(seqInput) | is.na(seqGL)) { return(NA) } seqI <- s2c(seqInput) seqG <- s2c(seqGL) matIGL <- matrix(c(seqI, seqG), ncol=length(seqI), nrow=2, byrow=T) mutations <- analyzeMutations2NucUri(matIGL) mutations <- mutations[!is.na(mutations)] #positions <- as.numeric(names(mutations)) # mutations <- mutations[positions <= VLENGTH] #remove the nucleotide mutations in the codons with multiple mutations if (multipleMutation == "ignore") { mutationCodons <- getCodonNumb(as.numeric(names(mutations))) tableMutationCodons <- table(mutationCodons) codonsWithMultipleMutations <- as.numeric(names(tableMutationCodons[tableMutationCodons>1])) mutations <- mutations[!(mutationCodons %in% codonsWithMultipleMutations)] } if (model == "s") { mutations <- mutations[mutations == "s"] } if (length(mutations) > 0) { return(mutations) } else { return(NA) } } # List the numbers of observed mutations # # This lists the observed number of mutation. # # @param db a data.frame of the DB file. # @param sequenceColumn The name of the sequence column. # @param germlineColumn The name of the germline column. # # @return list of mutations in each clone listObservedMutations <- function(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", multipleMutation=c("independent", "ignore"), model = c("rs", "s")) { # Make sure the columns specified exist if (!(sequenceColumn %in% names(db))) { stop("The sequence column", sequenceColumn, "was not found.") } if (!(germlineColumn %in% names(db))) { stop("The germline column", germlineColumn, "was not found.") } mutations <- mapply(listMutations, db[[sequenceColumn]], db[[germlineColumn]], multipleMutation, model, USE.NAMES=FALSE, SIMPLIFY = F) return(mutations) } #### Testing functions #### # Function to make dummy data for testing targetting functions # # @param nseq number of sequences # @param nmut number of mutations per sequence # @param nmer number of 5-mers per sequence (sequence length = 5 * nmer) # # @return a data.frame with columns sequence_id, sequence_alignment, germline_alignment_d_mask, v_call. # # @examples # db <- makeTargetingTestDb(500) makeTargetingTestDb <- function(nseq=10, nmut=40, nmers=50) { nuc_chars <- c("A", "C", "G", "T") .mut <- function(x, n) { i <- sample(1:nchar(x), n) y <- seqinr::s2c(x) y[i] <- sapply(y[i], function(z) sample(nuc_chars[nuc_chars != z], 1)) return(seqinr::c2s(y)) } seq <- apply(replicate(nseq, sample(seqinr::words(5, nuc_chars), nmers)), 2, paste, collapse="") germ <- sapply(seq, .mut, n=nmut) db <- data.frame(sequence_id=paste0("SEQ", 1:nseq), sequence_alignment=seq, germline_alignment_d_mask=germ, v_call="Homsap IGHV3-66*02 F", stringsAsFactors=FALSE) rownames(db) <- NULL return(db) } shazam/R/MutationProfiling.R0000644000176200001440000056052114506551517015555 0ustar liggesusers# Mutation profiling #' @include Shazam.R #' @include Core.R NULL #### Clonal consensus building functions #### #' Constructs effective clonal sequences for all clones #' #' \code{collapseClones} creates effective input and germline sequences for each clonal #' group and appends columns containing the consensus sequences to the input #' \code{data.frame}. #' #' @param db \code{data.frame} containing sequence data. Required. #' @param cloneColumn \code{character} name of the column containing clonal #' identifiers. Required. #' @param sequenceColumn \code{character} name of the column containing input #' sequences. Required. The length of each input sequence should #' match that of its corresponding germline sequence. #' @param germlineColumn \code{character} name of the column containing germline #' sequences. Required. The length of each germline sequence #' should match that of its corresponding input sequence. #' @param muFreqColumn \code{character} name of the column containing mutation #' frequency. Optional. Applicable to the \code{"mostMutated"} #' and \code{"leastMutated"} methods. If not supplied, mutation #' frequency is computed by calling \code{observedMutations}. #' Default is \code{NULL}. See Cautions for note on usage. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. Optional. Default is #' \code{NULL}. #' @param method method for calculating input consensus sequence. Required. #' One of \code{"thresholdedFreq"}, \code{"mostCommon"}, #' \code{"catchAll"}, \code{"mostMutated"}, or #' \code{"leastMutated"}. See "Methods" for details. #' @param minimumFrequency frequency threshold for calculating input consensus sequence. #' Applicable to and required for the \code{"thresholdedFreq"} #' method. A canonical choice is 0.6. Default is \code{NULL}. #' @param includeAmbiguous whether to use ambiguous characters to represent positions #' at which there are multiple characters with frequencies that #' are at least \code{minimumFrequency} or that are maximal #' (i.e. ties). Applicable to and required for the #' \code{"thresholdedFreq"} and \code{"mostCommon"} methods. #' Default is \code{FALSE}. See "Choosing ambiguous characters" #' for rules on choosing ambiguous characters. #' @param breakTiesStochastic In case of ties, whether to randomly pick a sequence from #' sequences that fulfill the criteria as consensus. Applicable #' to and required for all methods except for \code{"catchAll"}. #' Default is \code{FALSE}. See "Methods" for details. #' @param breakTiesByColumns A list of the form #' \code{list(c(col_1, col_2, ...), c(fun_1, fun_2, ...))}, #' where \code{col_i} is a \code{character} name of a column #' in \code{db}, and \code{fun_i} is a function to be applied #' on that column. Currently, only \code{max} and \code{min} #' are supported. Note that the two \code{c()}'s in \code{list()} #' are essential (i.e. if there is only 1 column, the list should #' be of the form \code{list(c(col_1), c(func_1))}. Applicable #' to and optional for the \code{"mostMutated"} and #' \code{"leastMutated"} methods. If supplied, \code{fun_i}'s #' are applied on \code{col_i}'s to help break ties. Default #' is \code{NULL}. See "Methods" for details. #' @param expandedDb \code{logical} indicating whether or not to return the #' expanded \code{db}, containing all the sequences (as opposed #' to returning just one sequence per clone). #' @param nproc Number of cores to distribute the operation over. If the #' \code{cluster} has already been set earlier, then pass the #' \code{cluster}. This will ensure that it is not reset. #' @param juncLengthColumn \code{character} name of the column containing the junction length. #' Needed when \code{regionDefinition} includes CDR3 and FWR4. #' @param fields additional fields used for grouping. Use sample_id, to #' avoid combining sequences with the same clone_id #' that belong to different sample_id. #' #' @return A modified \code{db} with the following additional columns: #' \itemize{ #' \item \code{clonal_sequence}: effective sequence for the clone. #' \item \code{clonal_germline}: germline sequence for the clone. #' \item \code{clonal_sequence_mufreq}: mutation frequency of #' \code{clonal_sequence}; only added for the \code{"mostMutated"} #' and \code{"leastMutated"} methods. #' } #' #' \code{clonal_sequence} is generated with the method of choice indicated #' by \code{method}, and \code{clonal_germline} is generated with the #' \code{"mostCommon"} method, along with, where applicable, user-defined #' parameters such as \code{minimumFrequency}, \code{includeAmbiguous}, #' \code{breakTiesStochastic}, and \code{breakTiesByColumns}. #' #' #' @section Consensus lengths: For each clone, \code{clonal_sequence} and #' \code{clonal_germline} have the same length. #' #' \itemize{ #' \item For the \code{"thresholdedFreq"}, \code{"mostCommon"}, and #' \code{"catchAll"} methods: #' #' The length of the consensus sequences is determined by the longest possible #' consensus sequence (baesd on \code{inputSeq} and \code{germlineSeq}) and #' \code{regionDefinition@seqLength} (if supplied), whichever is shorter. #' #' Given a set of sequences of potentially varying lengths, the longest possible #' length of their consensus sequence is taken to be the longest length along #' which there is information contained at every nucleotide position across #' majority of the sequences. Majority is defined to be greater than #' \code{floor(n/2)}, where \code{n} is the number of sequences. If the longest #' possible consensus length is 0, there will be a warning and an empty string #' (\code{""}) will be returned. #' #' If a length limit is defined by supplying a \code{regionDefinition} via #' \code{regionDefinition@seqLength}, the consensus length will be further #' restricted to the shorter of the longest possible length and #' \code{regionDefinition@seqLength}. #' #' \item For the \code{"mostMutated"} and \code{"leastMutated"} methods: #' #' The length of the consensus sequences depends on that of the most/least #' mutated input sequence, and, if supplied, the length limit defined by #' \code{regionDefinition@seqLength}, whichever is shorter. If the germline #' consensus computed using the \code{"mostCommon"} method is longer than #' the most/least mutated input sequence, the germline consensus is trimmed #' to be of the same length as the input consensus. #' #' } #' #' @section Methods: The descriptions below use "sequences" as a generalization of input #' sequences and germline sequences. #' #' \itemize{ #' #' \item \code{method="thresholdedFreq"} #' #' A threshold must be supplied to the argument \code{minimumFrequency}. At #' each position along the length of the consensus sequence, the frequency #' of each nucleotide/character across sequences is tabulated. The #' nucleotide/character whose frequency is at least (i.e. \code{>=}) #' \code{minimumFrequency} becomes the consensus; if there is none, the #' consensus nucleotide will be \code{"N"}. #' #' When there are ties (frequencies of multiple nucleotides/characters #' are at least \code{minimumFrequency}), this method can be deterministic #' or stochastic, depending on additional parameters. #' #' \itemize{ #' \item With \code{includeAmbiguous=TRUE}, ties are resolved #' deterministically by representing ties using ambiguous #' characters. See "Choosing ambiguous characters" for how #' ambiguous characters are chosen. #' \item With \code{breakTiesStochastic=TRUE}, ties are resolved #' stochastically by randomly picking a character amongst the #' ties. #' \item When both \code{TRUE}, \code{includeAmbiguous} takes #' precedence over \code{breakTiesStochastic}. #' \item When both \code{FALSE}, the first character from the ties is #' taken to be the consensus following the order of \code{"A"}, #' \code{"T"}, \code{"G"}, \code{"C"}, \code{"N"}, \code{"."}, #' and \code{"-"}. #' } #' #' Below are some examples looking at a single position based on 5 #' sequences with \code{minimumFrequency=0.6}, #' \code{includeAmbiguous=FALSE}, and \code{breakTiesStochastic=FALSE}: #' #' \itemize{ #' \item If the sequences have \code{"A"}, \code{"A"}, \code{"A"}, #' \code{"T"}, \code{"C"}, the consensus will be \code{"A"}, #' because \code{"A"} has frequency 0.6, which is at least #' \code{minimumFrequency}. #' \item If the sequences have \code{"A"}, \code{"A"}, \code{"T"}, #' \code{"T"}, \code{"C"}, the consensus will be \code{"N"}, #' because none of \code{"A"}, \code{"T"}, or \code{"C"} has #' frequency that is at least \code{minimumFrequency}. #' } #' #' \item \code{method="mostCommon"} #' #' The most frequent nucleotide/character across sequences at each #' position along the length of the consensus sequence makes up the consensus. #' #' When there are ties (multiple nucleotides/characters with equally #' maximal frequencies), this method can be deterministic or stochastic, #' depending on additional parameters. The same rules for breaking ties #' for \code{method="thresholdedFreq"} apply. #' #' Below are some examples looking at a single position based on 5 #' sequences with \code{includeAmbiguous=FALSE}, and #' \code{breakTiesStochastic=FALSE}: #' #' \itemize{ #' \item If the sequences have \code{"A"}, \code{"A"}, \code{"T"}, #' \code{"A"}, \code{"C"}, the consensus will be \code{"A"}. #' \item If the sequences have \code{"T"}, \code{"T"}, \code{"C"}, #' \code{"C"}, \code{"G"}, the consensus will be \code{"T"}, #' because \code{"T"} is before \code{"C"} in the order of #' \code{"A"}, \code{"T"}, \code{"G"}, \code{"C"}, \code{"N"}, #' \code{"."}, and \code{"-"}. #' } #' #' #' \item \code{method="catchAll"} #' #' This method returns a consensus sequence capturing most of the #' information contained in the sequences. Ambiguous characters are #' used where applicable. See "Choosing ambiguous characters" for how #' ambiguous characters are chosen. This method is deterministic and #' does not involve breaking ties. #' #' Below are some examples for \code{method="catchAll"} looking at a #' single position based on 5 sequences: #' #' \itemize{ #' \item If the sequences have \code{"N"}, \code{"N"}, \code{"N"}, #' \code{"N"}, \code{"N"}, the consensus will be \code{"N"}. #' \item If the sequences have \code{"N"}, \code{"A"}, \code{"A"}, #' \code{"A"}, \code{"A"}, the consensus will be \code{"A"}. #' \item If the sequences have \code{"N"}, \code{"A"}, \code{"G"}, #' \code{"A"}, \code{"A"}, the consensus will be \code{"R"}. #' \item If the sequences have \code{"-"}, \code{"-"}, \code{"."}, #' \code{"."}, \code{"."}, the consensus will be \code{"-"}. #' \item If the sequences have \code{"-"}, \code{"-"}, \code{"-"}, #' \code{"-"}, \code{"-"}, the consensus will be \code{"-"}. #' \item If the sequences have \code{"."}, \code{"."}, \code{"."}, #' \code{"."}, \code{"."}, the consensus will be \code{"."}. #' } #' #' \item \code{method="mostMutated"} and \code{method="leastMutated"} #' #' These methods return the most/least mutated sequence as the consensus #' sequence. #' #' When there are ties (multple sequences have the maximal/minimal mutation #' frequency), this method can be deterministic or stochastic, depending on #' additional parameters. #' #' \itemize{ #' \item With \code{breakTiesStochastic=TRUE}, ties are resolved #' stochastically by randomly picking a sequence out of #' sequences with the maximal/minimal mutation frequency. #' \item When \code{breakTiesByColumns} is supplied, ties are resolved #' deterministically. Column by column, a function is applied on #' the column and sequences with column value matching the #' functional value are retained, until ties are resolved or #' columns run out. In the latter case, the first remaining #' sequence is taken as the consensus. #' \item When \code{breakTiesStochastic=TRUE} and #' \code{breakTiesByColumns} is also supplied, #' \code{breakTiesStochastic} takes precedence over #' \code{breakTiesByColumns}. #' \item When \code{breakTiesStochastic=FALSE} and #' \code{breakTiesByColumns} is not supplied (i.e. \code{NULL}), #' the sequence that appears first amongst the ties is taken #' as the consensus. #' } #' #' } #' #' #' @section Choosing ambiguous characters: #' #' Ambiguous characters may be present in the returned consensuses when using the #' \code{"catchAll"} method and when using the \code{"thresholdedFreq"} or #' \code{"mostCommon"} methods with \code{includeAmbiguous=TRUE}. #' #' The rules on choosing ambiguous characters are as follows: #' #' \itemize{ #' \item If a position contains only \code{"N"} across sequences, the consensus #' at that position is \code{"N"}. #' \item If a position contains one or more of \code{"A"}, \code{"T"}, #' \code{"G"}, or \code{"C"}, the consensus will be an IUPAC character #' representing all of the characters present, regardless of whether #' \code{"N"}, \code{"-"}, or \code{"."} is present. #' \item If a position contains only \code{"-"} and \code{"."} across sequences, #' the consensus at that position is taken to be \code{"-"}. #' \item If a position contains only one of \code{"-"} or \code{"."} across #' sequences, the consensus at that position is taken to be the character #' present. #' } #' #' @section Cautions: #' #' \itemize{ #' \item Note that this function does not perform multiple sequence alignment. #' As a prerequisite, it is assumed that the sequences in #' \code{sequenceColumn} and \code{germlineColumn} have been aligned #' somehow. In the case of immunoglobulin repertoire analysis, this #' usually means that the sequences are IMGT-gapped. #' \item When using the \code{"mostMutated"} and \code{"leastMutated"} methods, #' if you supply both \code{muFreqColumn} and \code{regionDefinition}, #' it is your responsibility to ensure that the mutation frequency in #' \code{muFreqColumn} was calculated with sequence lengths restricted #' to the \strong{same} \code{regionDefinition} you are supplying. #' Otherwise, the "most/least mutated" sequence you obtain might not #' be the most/least mutated given the \code{regionDefinition} supplied, #' because your mutation frequency was based on a #' \code{regionDefinition} different from the one supplied. #' \item If you intend to run \code{collapseClones} before #' building a 5-mer targeting model, you \strong{must} choose #' parameters such that your collapsed clonal consensuses do #' \strong{not} include ambiguous characters. This is because the #' targeting model functions do NOT support ambiguous characters #' in their inputs. #' } #' #' @seealso #' See \link{IMGT_SCHEMES} for a set of predefined \link{RegionDefinition} objects. #' #' @examples #' # Subset example data #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d" & #' clone_id %in% c("3100", "3141", "3184")) #' #' # thresholdedFreq method, resolving ties deterministically without using ambiguous characters #' clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="thresholdedFreq", minimumFrequency=0.6, #' includeAmbiguous=FALSE, breakTiesStochastic=FALSE) #' #' # mostCommon method, resolving ties deterministically using ambiguous characters #' clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="mostCommon", #' includeAmbiguous=TRUE, breakTiesStochastic=FALSE) #' #' # Make a copy of db that has a mutation frequency column #' db2 <- observedMutations(db, frequency=TRUE, combine=TRUE) #' #' # mostMutated method, resolving ties stochastically #' clones <- collapseClones(db2, cloneColumn="clone_id", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="mostMutated", muFreqColumn="mu_freq", #' breakTiesStochastic=TRUE, breakTiesByColumns=NULL) #' #' # mostMutated method, resolving ties deterministically using additional columns #' clones <- collapseClones(db2, cloneColumn="clone_id", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="mostMutated", muFreqColumn="mu_freq", #' breakTiesStochastic=FALSE, #' breakTiesByColumns=list(c("duplicate_count"), c(max))) #' #' # Build consensus for V segment only #' # Capture all nucleotide variations using ambiguous characters #' clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="catchAll", regionDefinition=IMGT_V) #' #' # Return the same number of rows as the input #' clones <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' method="mostCommon", expandedDb=TRUE) #' #' @export collapseClones <- function(db, cloneColumn = "clone_id", sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", muFreqColumn = NULL, regionDefinition=NULL, method = c("mostCommon","thresholdedFreq","catchAll","mostMutated","leastMutated"), minimumFrequency = NULL, includeAmbiguous = FALSE, breakTiesStochastic = FALSE, breakTiesByColumns = NULL, expandedDb = FALSE, nproc = 1, juncLengthColumn="junction_length", fields=NULL) { # Hack for visibility of foreach index variables idx <- NULL ## DEBUG # cloneColumn="CLONE"; sequenceColumn="sequence_alignment"; germlineColumn="germline_alignment_d_mask" # expandedDb=FALSE; regionDefinition=NULL; method="mostCommon"; nproc=1 #### parameter checks method <- match.arg(method) # check minimumFrequency for thresholdedFreq method if (method=="thresholdedFreq") { if (!is.numeric(minimumFrequency)) { stop("minimumFrequency must be a numeric value.") } else { if ( minimumFrequency<0 | minimumFrequency>1 ) { stop("minimumFrequency must be between 0 and 1.") } } } # check includeAmbiguous & breakTiesStochastic for methods other than catchAll if (method %in% c("thresholdedFreq", "mostCommon", "mostMutated", "leastMutated")) { if (!is(includeAmbiguous, "logical")) { stop ("includeAmbiguous must be TRUE or FALSE.") } if (!is(breakTiesStochastic, "logical")) { stop ("breakTiesStochastic must be TRUE or FALSE.") } } # check breakTiesByColumns and muFreqColumn for methods most/leastMutated if (method %in% c("mostMutated", "leastMutated")) { if (!is.null(breakTiesByColumns)) { if (!is(breakTiesByColumns, "list")) { stop ("breakTiesByColumns must be a list.") } if (length(breakTiesByColumns) != 2) { stop ("breakTiesByColumns must be a nested list of length 2.") } if (length(breakTiesByColumns[[1]]) != length(breakTiesByColumns[[2]])) { stop ("Nested vectors in breakTiesByColumns must have the same lengths.") } if (!all(is.character(breakTiesByColumns[[1]]))) { stop ("The first vector in breakTiesByColumns must contain column names.") } if (!all( unlist( lapply(breakTiesByColumns[[2]], is.function)))) { stop ("The second vector in breakTiesByColumns must contain functions.") } if (!all(breakTiesByColumns[[1]] %in% colnames(db))) { stop ("All column named included in breakTiesByColumns must be present in db.") } } if ( (!is.null(muFreqColumn)) && (!muFreqColumn %in% colnames(db)) ) { stop ("If specified, muFreqColumn must be a column present in db.") } } # check mutual exclusivitiy if (method %in% c("thresholdedFreq", "mostCommon")){ if (includeAmbiguous & breakTiesStochastic) { message("includeAmbiguous and breakTiesStochastic are mutually exclusive. When both TRUE, includeAmbiguous will take precedence.") } #if ( (!includeAmbiguous) & (!breakTiesStochastic) ) { # message("When both includeAmbiguous and breakTiesStochastic are FALSE, ties are broken in the order of 'A', 'T', 'G', 'C', 'N', '.', and '-'.") #} if (!is.null(breakTiesByColumns)) { message("breakTiesByColumns is ignored when method is thresholdedFreq or mostCommon.") } } if (method %in% c("mostMutated", "leastMutated")){ if (breakTiesStochastic & !is.null(breakTiesByColumns)) { message("breakTiesStochastic and breakTiesByColumns are mutually exclusive. When both set, breakTiesStochastic will take precedence.") } #if ( (!breakTiesStochastic) & is.null(breakTiesByColumns) ) { # message("When breakTiesStochastic is FALSE and breakTiesByColumns is NULL, ties are broken by taking the sequence that appears earlier in the data.frame.") #} if (includeAmbiguous) { message("includeAmbiguous is ignored when method is mostMutated or leastMutated.") } } # Check for valid columns check <- checkColumns(db, c(cloneColumn, sequenceColumn, germlineColumn, fields)) if (check != TRUE) { stop(check) } # Check for NAs na_rows <- which(is.na(db[,c(cloneColumn, sequenceColumn, germlineColumn)] ), arr.ind=T) if (nrow(na_rows)>0) { na_cols <- c(cloneColumn, sequenceColumn, germlineColumn)[unique(na_rows[,2])] stop("NA values found in column(s): ", paste(na_cols, collapse=", "),". ",length(unique(na_rows[,1])), " sequence(s) affected.") } # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } ### Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn, germlineColumn)) # If the user has previously set the cluster and does not wish to reset it if(!is.numeric(nproc)){ cluster <- nproc nproc <- 0 } if (!is(expandedDb, "logical")) { stop ("expandedDb must be TRUE or FALSE.") } # Convert clone identifiers to strings db[[cloneColumn]] <- as.character(db[[cloneColumn]]) db$tmp_colclones_row_id <- 1:nrow(db) # use `fields` information to id clones db$fields_clone_id <- db %>% group_by(!!!rlang::syms(c(fields, cloneColumn))) %>% dplyr::group_indices() db$fields_clone_id <- as.character(db$fields_clone_id) # get row indices in db for each unique clone uniqueClones <- unique(db[["fields_clone_id"]]) # crucial to have simplify=FALSE (otherwise won't return a list if uniqueClones has length 1) uniqueClonesIdx <- sapply(uniqueClones, function(i){which(db[["fields_clone_id"]]==i)}, simplify=FALSE) regionDefinitionName <- "" if (!is.null(regionDefinition)) { regionDefinitionName <- regionDefinition@name } # if method is most/leastMutated and muFreqColumn not specified, # first calculate mutation frequency ($mu_freq) # IMPORTANT: do this OUTSIDE foreach loop for calcClonalConsensus # otherwise will get an error saying muFreqColumn not found in db # (something to do with parallelization/foreach) if ( (method %in% c("mostMutated", "leastMutated")) & is.null(muFreqColumn) ) { message("Calculating observed mutation frequency...") db <- observedMutations(db=db, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, regionDefinition=regionDefinition, frequency=TRUE, combine=TRUE, cloneColumn = "fields_clone_id", mutationDefinition=NULL, nproc=nproc) muFreqColumn <- "mu_freq" } # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } else { if (nproc != 0) { #cluster <- makeCluster(nproc, type="SOCK") cluster <- parallel::makeCluster(nproc, type= "PSOCK") } parallel::clusterExport(cluster, list('db', 'sequenceColumn', 'germlineColumn', 'muFreqColumn','juncLengthColumn', 'regionDefinition', 'regionDefinitionName', 'method', 'minimumFrequency','includeAmbiguous', 'breakTiesStochastic', 'breakTiesByColumns', 'calcClonalConsensus', 'consensusSequence', 'breakTiesHelper', 'chars2Ambiguous', 'nucs2IUPAC', 'IUPAC_DNA_2', 'NUCLEOTIDES_AMBIGUOUS', 'uniqueClonesIdx', 'c2s', 's2c','getCloneRegion'), envir=environment() ) registerDoParallel(cluster,cores=nproc) } # Printing status to console #cat("Collapsing clonal sequences...\n") # avoid .combine="cbind"! # if there is only 1 unique clone, .combind="cbind" will result in a vector (as opposed to # a matrix) being returned, which will subsequently result a failure in # cons_db$clonal_sequence <- cons_mat[, 1] cons_mat <- foreach(idx=1:length(uniqueClonesIdx), .verbose=FALSE, .errorhandling='stop') %dopar% { cloneIdx <- uniqueClonesIdx[[idx]] cloneDb <- db[cloneIdx, ] clone_num <- unique(cloneDb[['fields_clone_id']]) # Verify the assumption that all sequences in the clone have the same # junction length. if (length(unique(cloneDb[[juncLengthColumn]])) > 1 ) { stop("Expecting all sequences in the same clone with the same junction lenght.") } cloneRegionDefinition <- regionDefinition if (regionDefinitionName %in% c("IMGT_VDJ_BY_REGIONS","IMGT_VDJ")) { cloneRegionDefinition <- getCloneRegion(clone_num=clone_num, db=cloneDb, seq_col=sequenceColumn, juncLengthColumn=juncLengthColumn, clone_col='fields_clone_id', regionDefinition=regionDefinition) } # collapse clone calcClonalConsensus(db=cloneDb, sequenceColumn=sequenceColumn, germlineColumn=germlineColumn, muFreqColumn=muFreqColumn, regionDefinition=cloneRegionDefinition, method=method, minimumFrequency=minimumFrequency, includeAmbiguous=includeAmbiguous, breakTiesStochastic=breakTiesStochastic, breakTiesByColumns=breakTiesByColumns) } # using cbind below will give a matrix with columns being clones # use rbind to have rows be clones # cols: inputCons, germlineCons, inputMuFreq cons_mat <- do.call(rbind, cons_mat) # Stop cluster if(nproc > 1) { parallel::stopCluster(cluster) } # Build return data.frame if (expandedDb) { # Fill all rows with the consensus sequence clone_index <- match(db[["fields_clone_id"]], uniqueClones) cons_db <- db cons_db$clonal_sequence <- unlist(cons_mat[, 1])[clone_index] cons_db$clonal_germline <- unlist(cons_mat[, 2])[clone_index] # assign mutation frequency corresponding to consensus into clonal_sequence_mufreq if (method %in% c("mostMutated", "leastMutated")) { cons_db$clonal_sequence_mufreq <- unlist(cons_mat[, 3])[clone_index] } } else { # Return only the first row of each clone clone_index <- match(uniqueClones, db[["fields_clone_id"]]) cons_db <- db[clone_index, ] cons_db$clonal_sequence <- unlist(cons_mat[, 1]) cons_db$clonal_germline <- unlist(cons_mat[, 2]) # assign mutation frequency corresponding to consensus into clonal_sequence_mufreq if (method %in% c("mostMutated", "leastMutated")) { cons_db$clonal_sequence_mufreq <- unlist(cons_mat[, 3]) } } cons_db %>% arrange(!!rlang::sym("tmp_colclones_row_id")) %>% select(-!!rlang::sym("tmp_colclones_row_id"), -!!rlang::sym("fields_clone_id")) } # Break ties given additional columns in db and functions to compute on them # # @param idx vector of indices. # @param cols character vector of colnames. Currently, only columns containing # numeric values are supported/expected. # @param funs list of functions. Currently, only \code{max} and \code{min} are # supported/expected. # @param db \code{data.frame} containing columns named after \code{cols} with # corresponding rows for \code{idx}. # # @return a single value from \code{idx}. # # @details Column by column, \code{breakTiesHelper} calls the corresponding function # from \code{funs} on a column in \code{db} and finds the index/indices in # \code{idx} that match(es) the returned value from the function. This stops # when only a single matching index is obtained, or columns run out. In the # latter case, the first remaining index is returned. # # testing # expect index 18 # test.idx = c(2,4,18,37,102,76) # test.db = data.frame(cbind(DUPCOUNT= c(3,5,5,4,5,1), # CONSCOUNT=c(6,6,6,2,3,4), # ERR=c(0.9, 0.14, 0.12, 0.07, 0.3, 0.5))) # test.cols = c("DUPCOUNT", "CONSCOUNT", "ERR") # test.funs = c(max, max, min) # stopifnot( breakTiesHelper(test.idx, test.cols, test.funs, test.db)==18 ) # # make index 4 and 18 tie for ERR # # index 4 is expected because it appears before 18 # test.db[3,"ERR"] = 0.14 # stopifnot( breakTiesHelper(test.idx, test.cols, test.funs, test.db)==4 ) # breakTiesHelper <- function(idx, cols, funs, db) { # debug # idx=test.idx; cols=test.cols; funs=test.funs; db=test.db counter <- 1 while (length(idx)>1 & counter<=length(cols)) { cur.col <- cols[counter] cur.fun <- funs[[counter]] cur.db <- db[[cur.col]] target <- cur.fun(cur.db) tol <- 1e-5 # tolerance target.idx <- which( abs(cur.db-target)<=tol ) # wrt idx & db idx <- idx[target.idx] db <- db[target.idx, ] counter <- counter+1 } if (length(idx)==1) { return(idx) } else if (length(idx)>1) { #print("Failed to resolve ties.") # for testing/debugging return(idx[1]) } else { stop("breakTieHelper failed unexpectedly.") } } #' Construct a consensus sequence #' #' @param sequences character vector of sequences. #' @param db \code{data.frame} containing sequence data for a single clone. #' Applicable to and required for the \code{"mostMutated"} and #' \code{"leastMutated"} methods. Default is \code{NULL}. #' @param method method to calculate consensus sequence. One of #' \code{"thresholdedFreq"}, \code{"mostCommon"}, \code{"catchAll"}, #' \code{"mostMutated"}, or \code{"leastMutated"}. See "Methods" under #' \link{collapseClones} for details. #' @param minFreq frequency threshold for calculating input consensus sequence. #' Applicable to and required for the \code{"thresholdedFreq"} method. #' A canonical choice is 0.6. Default is \code{NULL}. #' @param muFreqColumn \code{character} name of the column in db containing mutation #' frequency. Applicable to and required for the \code{"mostMutated"} #' and \code{"leastMutated"} methods. Default is \code{NULL}. #' @param lenLimit limit on consensus length. if \code{NULL} then no length limit is set. #' @param includeAmbiguous whether to use ambiguous characters to represent positions at #' which there are multiple characters with frequencies that are at least #' \code{minimumFrequency} or that are maximal (i.e. ties). Applicable to #' and required for the \code{"thresholdedFreq"} and \code{"mostCommon"} #' methods. Default is \code{FALSE}. See "Choosing ambiguous characters" #' under \link{collapseClones} for rules on choosing ambiguous characters. #' Note: this argument refers to the use of ambiguous nucleotides in the #' output consensus sequence. Ambiguous nucleotides in the input sequences #' are allowed for methods catchAll, mostMutated and leastMutated. #' @param breakTiesStochastic In case of ties, whether to randomly pick a sequence from sequences that #' fulfill the criteria as consensus. Applicable to and required for all methods #' except for \code{"catchAll"}. Default is \code{FALSE}. See "Methods" #' under \link{collapseClones} for details. #' @param breakTiesByColumns A list of the form \code{list(c(col_1, col_2, ...), c(fun_1, fun_2, ...))}, #' where \code{col_i} is a \code{character} name of a column in \code{db}, #' and \code{fun_i} is a function to be applied on that column. Currently, #' only \code{max} and \code{min} are supported. Note that the two \code{c()}'s #' in \code{list()} are essential (i.e. if there is only 1 column, the list #' should be of the form \code{list(c(col_1), c(func_1))}. Applicable to and #' optional for the \code{"mostMutated"} and \code{"leastMutated"} methods. #' If supplied, \code{fun_i}'s are applied on \code{col_i}'s to help break #' ties. Default is \code{NULL}. See "Methods" under \link{collapseClones} #' for details. #' #' @return A list containing \code{cons}, which is a character string that is the consensus sequence #' for \code{sequences}; and \code{muFreq}, which is the maximal/minimal mutation frequency of #' the consensus sequence for the \code{"mostMutated"} and \code{"leastMutated"} methods, or #' \code{NULL} for all other methods. #' #' @details See \link{collapseClones} for detailed documentation on methods and additional parameters. #' #' @examples #' # Subset example data #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d") #' clone <- subset(db, clone_id == "3192") #' #' # First compute mutation frequency for most/leastMutated methods #' clone <- observedMutations(clone, frequency=TRUE, combine=TRUE) #' #' # Manually create a tie #' clone <- rbind(clone, clone[which.max(clone$mu_freq), ]) #' #' # ThresholdedFreq method. #' # Resolve ties deterministically without using ambiguous characters #' cons1 <- consensusSequence(clone$sequence_alignment, #' method="thresholdedFreq", minFreq=0.3, #' includeAmbiguous=FALSE, #' breakTiesStochastic=FALSE) #' cons1$cons #' #' @export ## DEBUG # thresholdedFreq method, resolve ties deterministically using ambiguous characters # consInput2 <- consensusSequence(clone$sequence_alignment, # muFreqColumn=NULL, lenLimit=NULL, # method="thresholdedFreq", minFreq=0.3, # includeAmbiguous=TRUE, # breakTiesStochastic=FALSE, # breakTiesByColumns=NULL, db=NULL)$cons # thresholdedFreq method, resolve ties stochastically # consInput3 <- consensusSequence(clone$sequence_alignment, # muFreqColumn=NULL, lenLimit=NULL, # method="thresholdedFreq", minFreq=0.3, # includeAmbiguous=FALSE, # breakTiesStochastic=TRUE, # breakTiesByColumns=NULL, db=NULL)$cons # mostCommon method, resolve ties deterministically without using ambiguous characters # consInput4 <- consensusSequence(clone$sequence_alignment, # muFreqColumn=NULL, lenLimit=NULL, # method="mostCommon", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=NULL, db=NULL)$cons # mostCommon method, resolve ties deterministically using ambiguous characters # consInput5 <- consensusSequence(clone$sequence_alignment, # muFreqColumn=NULL, lenLimit=NULL, # method="mostCommon", minFreq=NULL, # includeAmbiguous=TRUE, # breakTiesStochastic=FALSE, # breakTiesByColumns=NULL, db=NULL)$cons # mostCommon method, resolve ties stochastically # consInput6 <- consensusSequence(clone$sequence_alignment, # muFreqColumn=NULL, lenLimit=NULL, # method="mostCommon", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=TRUE, # breakTiesByColumns=NULL, db=NULL)$cons # catchAll method # consInput7 <- consensusSequence(clone$sequence_alignment, # muFreqColumn=NULL, lenLimit=NULL, # method="catchAll", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=NULL, db=NULL)$cons # mostMutated method, resolve ties stochastically # consInput8 <- consensusSequence(clone$sequence_alignment, # muFreqColumn="mu_freq", lenLimit=NULL, # method="mostMutated", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=TRUE, # breakTiesByColumns=NULL, db=clone)$cons # mostMutated method, resolve ties deterministically using additional columns # consInput9 <- consensusSequence(clone$sequence_alignment, # muFreqColumn="mu_freq", lenLimit=NULL, # method="mostMutated", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=list(c("junction_length","duplicate_count"), c(max, max)), # db=clone)$cons # consInput10 <- consensusSequence(clone$sequence_alignment, # muFreqColumn="mu_freq", lenLimit=NULL, # method="mostMutated", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=list(c("duplicate_count"), c(max)), # db=clone)$cons # mostMutated method, resolve ties deterministically withou using additional columns # consInput11 <- consensusSequence(clone$sequence_alignment, # muFreqColumn="mu_freq", lenLimit=NULL, # method="mostMutated", minFreq=NULL, # includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=NULL, db=clone)$cons consensusSequence <- function(sequences, db=NULL, method=c("mostCommon", "thresholdedFreq", "catchAll", "mostMutated", "leastMutated"), minFreq=NULL, muFreqColumn=NULL, lenLimit=NULL,includeAmbiguous=FALSE, breakTiesStochastic=FALSE, breakTiesByColumns=NULL) { # Check arguments method <- match.arg(method) # check muFreqColumn and get muFreq for most/leastMutated if (method %in% c("mostMutated", "leastMutated")) { if ( is.null(muFreqColumn) ) { stop ("muFreqColumn must be specified when method is most/leastMutated.") } if ( is.null(db) ) { stop ("db containing muFreqColumn must be supplied when method is most/leastMutated.") } if (!muFreqColumn %in% colnames(db)) { print(c("Helper", muFreqColumn)) print(c("Helper", colnames(db))) stop ("muFreqColumn must be a column present in db.") } # get muFreq muFreq <- db[[muFreqColumn]] } numSeqs <- length(sequences) ##### if only one sequence in clone, return it if (numSeqs==1) { # restrict length if there is a lenLimit if (!is.null(lenLimit)) { consensus <- substr(sequences, 1, min(lenLimit, stri_length(sequences))) } else { # otherwise, return as is consensus <- sequences } # return with mutation frequency (if applicable) if (method %in% c("mostMutated", "leastMutated")) { return(list(cons=consensus, muFreq=db[[muFreqColumn]])) } else { return(list(cons=consensus, muFreq=NULL)) } } ##### if all sequences are the same, return now if (length(unique(sequences))==1) { # restrict length if there is a lenLimit if (!is.null(lenLimit)) { consensus <- substr(sequences[1], 1, min(lenLimit, stri_length(sequences))) } else { # otherwise, return as is consensus <- sequences[1] } # return with mutation frequency (if applicable) if (method %in% c("mostMutated", "leastMutated")) { return(list(cons=consensus, muFreq=db[[muFreqColumn]][1])) } else { return(list(cons=consensus, muFreq=NULL)) } } ##### length of longest sequence in sequences lenSeqs <- stri_length(sequences) lenMax <- max(lenSeqs, na.rm=T) ##### methods = thresholdedFreq, mostCommon, catchAll if (method %in% c("thresholdedFreq", "mostCommon", "catchAll")) { ##### convert sequences to a matrix # if there's no more nucleotide when a seq ends, fill position with NA seqsMtx <- matrix(NA, nrow=numSeqs, ncol=lenMax) for (i in 1:numSeqs) { seqsMtx[i, 1:lenSeqs[i]] <- s2c(sequences[i]) } ##### tabulation matrix # col: nucleotide position # row: A,T,G,C,N,.,-,na (to distinguish from NA) if (method != "catchAll") { tabMtxRownames <- c("A","T","G","C","N",".","-","na") } else { # Allow for input ambiguous characters tabMtxRownames <- c(NUCLEOTIDES_AMBIGUOUS,"na") } tabMtx <- matrix(0, ncol=lenMax, nrow=length(tabMtxRownames), dimnames=list(tabMtxRownames, NULL)) ## across sequences, at each nuc position, how many A, T, G, C, N, ., -? # this does not capture NA # for (j in 1:ncol(seqsMtx)) { # tab <- table(seqsMtx[, j]) # r <- match(names(tab), tabMtxRownames) # if (any(is.na(r))) { # stop("Ambiguous nucleotides or unexpected characters found in `sequences`.") # } # tabMtx[r, j] <- tab # } # This is faster: if (!all(na.omit(as.vector(seqsMtx)) %in% tabMtxRownames)) { stop("Ambiguous nucleotides or unexpected characters found in `sequences`.") } tabMtx <- apply(seqsMtx, 2, function(j) { sapply(tabMtxRownames, function(nt){ sum(j == nt, na.rm=T) }) }) ## across sequences, at each nuc position, how many NAs? numNAs <- colSums(is.na(seqsMtx)) tabMtx["na", ] <- numNAs # sanity check: counts at each nuc pos (colSum) should sum up to number of sequences stopifnot( sum( colSums(tabMtx)==numSeqs ) == ncol(tabMtx) ) ##### only keep positions at which majority of sequences contain information ### if there are odd number of n sequences, keep position if it has > floor(n/2) non-NAs # e.g. 5 input sequences, >2 non-NA; 2=floor(5/2) ### if there are even number of n sequences, keep position if it has > n/2 non-NAs # e.g. 6 input sequences, >3 non-NA; 3=6/2=floor(6/2) numNonNAs <- numSeqs - numNAs nonNA.keep <- numNonNAs > floor(numSeqs/2) # length of longest possible consensus seq lenConsensus <- sum(nonNA.keep) if (lenConsensus==0) { warning("Consensus cannot be produced. Empty string returned.") return("") } ##### if there is a lenLimit, restrict consensus length to # the shorter of longest possible length and lenLimit if (!is.null(lenLimit)) { lenConsensus <- min(lenConsensus, lenLimit) } # drop=FALSE so that it works even with lenConsensus of 1 tabMtx <- tabMtx[, 1:lenConsensus, drop=FALSE] ### convert absolute count to fraction tabMtx <- tabMtx/numSeqs # remove "na" row # drop=FALSE so that it works even with lenConsensus of 1 tabMtx <- tabMtx[-which(rownames(tabMtx)=="na"), , drop=FALSE] if (method=="thresholdedFreq") { #print(method) # for testing # use as.matrix so that apply won't break with ncol(tabMtx)=1 consensus <- apply(as.matrix(tabMtx), 2, function(x){ idx <- which(x >= minFreq) # if no character >= the threshold, assign an N if (length(idx)==0) { return("N") # if there is no tie } else if (length(idx)==1){ return(names(x)[idx]) # if there are ties (multiple characters >= the threhold) } else if (length(idx)>1) { # ambiguous character allowed if (includeAmbiguous) { return(chars2Ambiguous(tabMtxRownames[idx])) # ambiguous characters not allowed } else { # stochastic if (breakTiesStochastic) { return(names(x)[sample(x=idx, size=1)]) # first one is returned # the order is built-in from tabMtxRownames } else { return(names(x)[idx[1]]) } } } }) } else if (method=="mostCommon") { #print(method) # for testing # use as.matrix so that apply won't break with ncol(tabMtx)=1 consensus <- apply(as.matrix(tabMtx), 2, function(x){ max.freq <- max(x) tol <- 1e-5 # tolerance max.idx <- which( abs(x-max.freq)<=tol ) # if there is no tie if (length(max.idx)==1){ return(names(x)[max.idx]) # if there are ties (multiple characters with maximal frequency) } else if (length(max.idx)>1) { # ambiguous character allowed if (includeAmbiguous) { return(chars2Ambiguous(tabMtxRownames[max.idx])) # ambiguous characters not allowed } else { # stochastic if (breakTiesStochastic) { return(names(x)[sample(x=max.idx, size=1)]) # first one is returned # the order is built-in from tabMtxRownames } else { return(names(x)[max.idx[1]]) } } } }) } else if (method=="catchAll") { #print(method) # for testing # use as.matrix so that apply won't break with ncol(tabMtx)=1 consensus <- apply(as.matrix(tabMtx), 2, function(x){ # all characters that appear at a position across sequences nonZeroNucs <- rownames(tabMtx)[x>0] # Disambiguate, except N nonZeroNucs <- unique(unlist(c(IUPAC_DNA[names(IUPAC_DNA)!="N"],"."=".","-"="-","N"="N")[nonZeroNucs])) # convert characters to (ambiguous) characters return(chars2Ambiguous(nonZeroNucs)) }) } # check there is no ambiguous characters if includeAmbiguous if F if ( (method=="thresholdedFreq" | method=="mostCommon") & !includeAmbiguous ) { ambiguous <- NUCLEOTIDES_AMBIGUOUS[!NUCLEOTIDES_AMBIGUOUS %in% c("A","C","G","T","N","-",".")] stopifnot( !any(consensus %in% ambiguous) ) } # convert from character vector to string consensus <- c2s(consensus) # sanity check stopifnot( stri_length(consensus)==lenConsensus ) } ##### methods = mostMutated, leastMutated if (method %in% c("mostMutated", "leastMutated")) { # if there's a lenLimit # if a seq is longer than lenLimit, trim it; otherwise, leave it as is if (!is.null(lenLimit)) { idxLong <- which(lenSeqs > lenLimit) sequences[idxLong] <- substr(sequences[idxLong], 1, lenLimit) } ##### get index of sequences that fulfill the criterion # muFreq should have been calculated being on sequences with restricted lengths as defined by # regionDefinition (which gives rise to lenLimit) if (method=="mostMutated") { #print(method) # for testing targetMuFreq <- max(muFreq) } else if (method=="leastMutated") { #print(method) # for testing targetMuFreq <- min(muFreq) } tol <- 1e-5 # tolerance idx <- which( abs(muFreq-targetMuFreq)<=tol ) ##### if there are no ties if (length(idx)==1) { consensus <- sequences[idx] ##### if there are ties } else if (length(idx)>1) { ### stochastic: randomly pick one from idx if (breakTiesStochastic) { consensus <- sequences[sample(x=idx, size=1)] ### deterministic: pick one from idx based on breakTiesByColumns } else if (!is.null(breakTiesByColumns)) { idx <- breakTiesHelper(idx=idx, cols=breakTiesByColumns[[1]], funs=breakTiesByColumns[[2]], db=db[idx, ]) consensus <- sequences[idx] ### deterministic: pick first one from idx } else { consensus <- sequences[idx[1]] } } } # check length if (!is.null(lenLimit)) { stopifnot(stri_length(consensus) <= lenLimit) } if (method %in% c("mostMutated", "leastMutated")) { return(list(cons=consensus, muFreq=targetMuFreq)) } else { return(list(cons=consensus, muFreq=NULL)) } } # Calculate clonal consensus for a single clone # # Given an aligned set of input/observed sequences and an aligned set of germline sequences, # generate an input/observed consensus and a germline consensus. # # @param db \code{data.frame} containing sequence data for a single clone. # Required. # @param sequenceColumn \code{character} name of the column containing input # sequences. Required. The length of each input sequence should # match that of its corresponding germline sequence. # @param germlineColumn \code{character} name of the column containing germline # sequences. Required. The length of each germline sequence should # match that of its corresponding input sequence. # @param muFreqColumn \code{character} name of the column containing mutation # frequency. Applicable to and required for the \code{"mostMutated"} # and \code{"leastMutated"} methods. Default is \code{NULL}. See # "Details" for a note of caution. # @param regionDefinition \link{RegionDefinition} object defining the regions and boundaries # of the Ig sequences. Optional. Default is \code{NULL}. # @param method method for calculating input consensus sequence. Required. One of # \code{"thresholdedFreq"}, \code{"mostCommon"}, \code{"catchAll"}, # \code{"mostMutated"}, or \code{"leastMutated"}. See "Methods" under # \link{collapseClones} for details. # @param minimumFrequency frequency threshold for calculating input consensus sequence. # Applicable to and required for the \code{"thresholdedFreq"} method. # A canonical choice is 0.6. Default is \code{NULL}. # @param includeAmbiguous whether to use ambiguous characters to represent positions at # which there are multiple characters with frequencies that are at least # \code{minimumFrequency} or that are maximal (i.e. ties). Applicable to # and required for the \code{"thresholdedFreq"} and \code{"mostCommon"} # methods. Default is \code{FALSE}. See "Choosing ambiguous characters" # under \link{collapseClones} for rules on choosing ambiguous characters. # @param breakTiesStochastic In case of ties, whether to randomly pick a sequence from sequences that # fulfill the criteria as consensus. Applicable to and required for all methods # except for \code{"catchAll"}. Default is \code{FALSE}. See "Methods" # under \link{collapseClones} for details. # @param breakTiesByColumns A list of the form \code{list(c(col_1, col_2, ...), c(fun_1, fun_2, ...))}, # where \code{col_i} is a \code{character} name of a column in \code{db}, # and \code{fun_i} is a function to be applied on that column. Currently, # only \code{max} and \code{min} are supported. Applicable to and optional for # the \code{"mostMutated"} and \code{"leastMutated"} methods. If supplied, # \code{fun_i}'s are applied on \code{col_i}'s to help break ties. Default is # \code{NULL}. See "Methods" under \link{collapseClones} for details. # # @return A named list of length 3. "inputCons" and "germlineCons" are the consensus sequences. # The input and germline consensus sequences have the same length. "inputMuFreq" is the # maximal/minimal mutation frequency for the input consensus for the \code{"mostMutated"} # and \code{"leastMutated"} methods, and \code{NULL} for all other methods. # # @details See \link{collapseClones} for detailed documention on methods and additional parameters. # # Caution: when using the \code{"mostMutated"} and \code{"leastMutated"} methods, if you # supply a \code{regionDefinition}, it is your responsibility to ensure that the mutation # frequency in\code{muFreqColumn} was calculated with sequence lengths restricted to the # \strong{same} \code{regionDefinition} you are supplying. Otherwise, the # "most/least mutated" sequence you obtain might not be the most/least mutated given the # \code{regionDefinition} supplied, because your mutation frequency was based on a # \code{regionDefinition} different from the one supplied. # # @seealso # See \link{collapseClones} for constructing consensus for all clones. # # @examples # # Subset example data # data(ExampleDb, package="alakazam") # db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d") # # # Data corresponding to a single clone # clone <- db[db[["clone_id"]] == "3192", ] # # Number of sequences in this clone # nrow(clone) # # compute mutation frequency for most/leastMutated methods # clone <- observedMutations(db=clone, frequency=TRUE, combine=TRUE) # # manually create a tie # clone <- rbind(clone, clone[which.max(clone$mu_freq), ]) # # # Get consensus input and germline sequences # # thresholdedFreq method, resolve ties deterministically without using ambiguous characters # cons1 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="thresholdedFreq", # minimumFrequency=0.3, includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, breakTiesByColumns=NULL) # # thresholdedFreq method, resolve ties deterministically using ambiguous characters # cons2 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="thresholdedFreq", # minimumFrequency=0.3, includeAmbiguous=TRUE, # breakTiesStochastic=FALSE, breakTiesByColumns=NULL) # # thresholdedFreq method, resolve ties stochastically # cons3 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="thresholdedFreq", # minimumFrequency=0.3, includeAmbiguous=FALSE, # breakTiesStochastic=TRUE, breakTiesByColumns=NULL) # # mostCommon method, resolve ties deterministically without using ambiguous characters # cons4 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="mostCommon", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, breakTiesByColumns=NULL) # # mostCommon method, resolve ties deterministically using ambiguous characters # cons5 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="mostCommon", # minimumFrequency=NULL, includeAmbiguous=TRUE, # breakTiesStochastic=FALSE, breakTiesByColumns=NULL) # # mostCommon method, resolve ties stochastically # cons6 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="mostCommon", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=TRUE, breakTiesByColumns=NULL) # # catchAll method # cons7 <- calcClonalConsensus(db=clone, # muFreqColumn=NULL, regionDefinition=NULL, # method="catchAll", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, breakTiesByColumns=NULL) # # mostMutated method, resolve ties stochastically # cons8 <- calcClonalConsensus(db=clone, # muFreqColumn="mu_freq", regionDefinition=NULL, # method="mostMutated", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=TRUE, breakTiesByColumns=NULL) # # mostMutated method, resolve ties deterministically using additional columns # cons9 <- calcClonalConsensus(db=clone, # muFreqColumn="mu_freq", regionDefinition=NULL, # method="mostMutated", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=list(c("junction_length", "duplicate_count"), c(max, max))) # cons10 <- calcClonalConsensus(db=clone, # muFreqColumn="mu_freq", regionDefinition=NULL, # method="mostMutated", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, # breakTiesByColumns=list(c("duplicate_count"), c(max))) # # mostMutated method, resolve ties deterministically without using additional columns # cons11 <- calcClonalConsensus(db=clone, # muFreqColumn="mu_freq", regionDefinition=NULL, # method="mostMutated", # minimumFrequency=NULL, includeAmbiguous=FALSE, # breakTiesStochastic=FALSE, breakTiesByColumns=NULL) # @export calcClonalConsensus <- function(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", muFreqColumn=NULL, regionDefinition=NULL, method=c("mostCommon", "thresholdedFreq", "catchAll", "mostMutated", "leastMutated"), minimumFrequency=NULL, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, breakTiesByColumns=NULL) { method <- match.arg(method) inputSeq <- db[[sequenceColumn]] germlineSeq <- db[[germlineColumn]] # length of seqs in inputSeq and those in germlineSeq should match if ( sum(stri_length(inputSeq)==stri_length(germlineSeq)) != length(inputSeq) ) { stop("Sequences in inputSeq and germlineSeq have different lengths.") } # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # length limit from regionDefinition if (!is.null(regionDefinition)) { lenRegion <- regionDefinition@seqLength } else { lenRegion <- NULL } ##### get consensus germline sequence (most common) # NULL for minFreq and muFreqColumn b/c mostCommon definitely doesn't need them germCons <- consensusSequence(germlineSeq, minFreq=NULL, lenLimit=lenRegion, method="mostCommon", includeAmbiguous=includeAmbiguous, breakTiesStochastic=breakTiesStochastic, breakTiesByColumns=NULL, muFreqColumn=NULL, db=NULL)$cons ##### get consensus observed sequence inputConsMuFreq <- consensusSequence(inputSeq, minFreq=minimumFrequency, lenLimit=lenRegion, method=method, includeAmbiguous=includeAmbiguous, breakTiesStochastic=breakTiesStochastic, breakTiesByColumns=breakTiesByColumns, muFreqColumn=muFreqColumn, db=db) inputCons <- inputConsMuFreq$cons inputMuFreq <- inputConsMuFreq$muFreq if (method %in% c("mostMutated", "leastMutated")) { # possible to have inputCons and germCons of varying lengths # germCons (mostCommon) length is "longest possible length" for mostCommon # inputCons length is min of length of most/least mutated and lenLimit # if different, trim the two to same length lenInput <- stri_length(inputCons) lenGerm <- stri_length(germCons) if (lenInput != lenGerm) { minLen <- min(lenInput, lenGerm) inputCons <- substr(inputCons, 1, minLen) germCons <- substr(germCons, 1, minLen) } } # sanity check: length of germCons and inputCons should be the same # all methods other than most/leastMutated should expect same lengths of inputCons & germCons stopifnot( stri_length(germCons)==stri_length(inputCons) ) return(list("inputCons"=inputCons, "germlineCons"=germCons, "inputMuFreq"=inputMuFreq)) } #### Mutation counting functions #### #' Calculate observed numbers of mutations #' #' \code{observedMutations} calculates the observed number of mutations for each #' sequence in the input \code{data.frame}. #' #' @param db \code{data.frame} containing sequence data. #' @param sequenceColumn \code{character} name of the column containing input #' sequences. IUPAC ambiguous characters for DNA are #' supported. #' @param germlineColumn \code{character} name of the column containing #' the germline or reference sequence. IUPAC ambiguous #' characters for DNA are supported. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. If NULL, mutations #' are counted for entire sequence. To use regions definitions, #' sequences in \code{sequenceColum} and \code{germlineColumn} #' must be aligned, following the IMGT schema. #' @param mutationDefinition \link{MutationDefinition} object defining replacement #' and silent mutation criteria. If \code{NULL} then #' replacement and silent are determined by exact #' amino acid identity. #' @param ambiguousMode whether to consider ambiguous characters as #' \code{"either or"} or \code{"and"} when determining and #' counting the type(s) of mutations. Applicable only if #' \code{sequenceColumn} and/or \code{germlineColumn} #' contain(s) ambiguous characters. One of #' \code{c("eitherOr", "and")}. Default is \code{"eitherOr"}. #' @param frequency \code{logical} indicating whether or not to calculate #' mutation frequencies. Default is \code{FALSE}. #' @param combine \code{logical} indicating whether for each sequence should #' the mutation counts for the different regions (CDR, FWR) and #' mutation types be combined and return one value of #' count/frequency per sequence instead of #' multiple values. Default is \code{FALSE}. #' @param nproc number of cores to distribute the operation over. If the #' cluster has already been set the call function with #' \code{nproc} = 0 to not reset or reinitialize. Default is #' \code{nproc} = 1. #' @param cloneColumn clone id column name in \code{db} #' @param juncLengthColumn junction length column name in \code{db} #' #' @return A modified \code{db} \code{data.frame} with observed mutation counts for each #' sequence listed. The columns names are dynamically created based on the #' regions in the \code{regionDefinition}. For example, when using the #' \link{IMGT_V} definition, which defines positions for CDR and #' FWR, the following columns are added: #' \itemize{ #' \item \code{mu_count_cdr_r}: number of replacement mutations in CDR1 and #' CDR2 of the V-segment. #' \item \code{mu_count_cdr_s}: number of silent mutations in CDR1 and CDR2 #' of the V-segment. #' \item \code{mu_count_fwr_r}: number of replacement mutations in FWR1, #' FWR2 and FWR3 of the V-segment. #' \item \code{mu_count_fwr_s}: number of silent mutations in FWR1, FWR2 and #' FWR3 of the V-segment. #' } #' If \code{frequency=TRUE}, R and S mutation frequencies are #' calculated over the number of non-N positions in the specified regions. #' \itemize{ #' \item \code{mu_freq_cdr_r}: frequency of replacement mutations in CDR1 and #' CDR2 of the V-segment. #' \item \code{mu_freq_cdr_s}: frequency of silent mutations in CDR1 and CDR2 #' of the V-segment. #' \item \code{mu_freq_fwr_r}: frequency of replacement mutations in FWR1, #' FWR2 and FWR3 of the V-segment. #' \item \code{mu_freq_fwr_s}: frequency of silent mutations in FWR1, FWR2 and #' FWR3 of the V-segment. #' } #' If \code{frequency=TRUE} and \code{combine=TRUE}, the mutations and non-N positions #' are aggregated and a single \code{mu_freq} value is returned #' \itemize{ #' \item \code{mu_freq}: frequency of replacement and silent mutations in the #' specified region #' } #' #' @details #' Mutation counts are determined by comparing a reference sequence to the input sequences in the #' column specified by \code{sequenceColumn}. See \link{calcObservedMutations} for more technical details, #' \strong{including criteria for which sequence differences are included in the mutation #' counts and which are not}. #' #' The mutations are binned as either replacement (R) or silent (S) across the different #' regions of the sequences as defined by \code{regionDefinition}. Typically, this would #' be the framework (FWR) and complementarity determining (CDR) regions of IMGT-gapped #' nucleotide sequences. Mutation counts are appended to the input \code{db} as #' additional columns. #' #' If \code{db} includes lineage information, such as the \code{parent_sequence} column created by #' \link{makeGraphDf}, the reference sequence can be set to use that field as reference sequence #' using the \code{germlineColumn} argument. #' #' @seealso #' \link{calcObservedMutations} is called by this function to get the number of mutations #' in each sequence grouped by the \link{RegionDefinition}. #' See \link{IMGT_SCHEMES} for a set of predefined \link{RegionDefinition} objects. #' See \link{expectedMutations} for calculating expected mutation frequencies. #' See \link{makeGraphDf} for creating the field \code{parent_sequence}. #' #' @examples #' # Subset example data #' data(ExampleDb, package="alakazam") #' db <- ExampleDb[1:10, ] #' #' # Calculate mutation frequency over the entire sequence #' db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' frequency=TRUE, #' nproc=1) #' #' # Count of V-region mutations split by FWR and CDR #' # With mutations only considered replacement if charge changes #' db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' regionDefinition=IMGT_V, #' mutationDefinition=CHARGE_MUTATIONS, #' nproc=1) #' #' # Count of VDJ-region mutations, split by FWR and CDR #' db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' regionDefinition=IMGT_VDJ, #' nproc=1) #' #' # Extend data with lineage information #' data(ExampleTrees, package="alakazam") #' graph <- ExampleTrees[[17]] #' clone <- alakazam::makeChangeoClone(subset(ExampleDb, clone_id == graph$clone)) #' gdf <- makeGraphDf(graph, clone) #' #' # Count of mutations between observed sequence and immediate ancenstor #' db_obs <- observedMutations(gdf, sequenceColumn="sequence", #' germlineColumn="parent_sequence", #' regionDefinition=IMGT_VDJ, #' nproc=1) #' #' @export observedMutations <- function(db,sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment_d_mask", regionDefinition=NULL, mutationDefinition = NULL, ambiguousMode = c("eitherOr", "and"), frequency = FALSE, combine = FALSE, nproc = 1, cloneColumn = "clone_id", juncLengthColumn = "junction_length") { ambiguousMode <- match.arg(ambiguousMode) check <- checkColumns(db, c(sequenceColumn, germlineColumn)) if (check != TRUE) { stop(check) } regionDefinitionName <- "" if (!is.null(regionDefinition)) { regionDefinitionName <- regionDefinition@name # Message if sequences don't have gaps or Ns (because makeChangeo clone # masks IMGT gaps) as a proxy to detect not IMGT aligned sequences if (all(!grepl("[\\.Nn]",db[[sequenceColumn]]))) { warning("No IMGT gaps detected in ",sequenceColumn,".\nSequences in ", sequenceColumn," and ", germlineColumn, " should be aligned, with gaps (.,N or n) following the IMGT numbering scheme.") } if (all(!grepl("[\\.Nn]",db[[germlineColumn]]))) { warning("No IMGT gaps detected in ",germlineColumn, ".\nSequences in ", sequenceColumn," and ", germlineColumn, " should be aligned, with gaps (., N or n) following the IMGT numbering scheme.") } not_na <- !is.na(db[[germlineColumn]]) if (!all.equal(nchar(db[[sequenceColumn]][not_na]), nchar(db[[germlineColumn]][not_na]))) { warning("Pairs of ", sequenceColumn, " and ", germlineColumn, " sequences with different lengths found.") stop("Expecting IMGT aligned, same length sequences in ", sequenceColumn, " and ", germlineColumn,".") } } # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Check if mutation count/freq columns already exist # and throw overwritting warning if (!is.null(regionDefinition)) { labels <- regionDefinition@labels } else { labels <- makeNullRegionDefinition()@labels } if (frequency == TRUE) { if (combine) { labels <- "mu_freq" } else { labels <- paste("mu_freq_", labels, sep="") } } else { if (combine) { labels <- "mu_count" } else { labels <- paste("mu_count_", labels, sep="") } } label_exists <- labels[labels %in% colnames(db)] if (length(label_exists)>0) { warning(paste0("Columns ", paste(label_exists, collapse=", "), " exist and will be overwritten") ) db[,label_exists] <- NULL } # Check mutation definition if (!is.null(mutationDefinition) & !is(mutationDefinition, "MutationDefinition")) { stop(deparse(substitute(mutationDefinition)), " is not a valid MutationDefinition object") } # Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn, germlineColumn)) db$tmp_obsmu_row_id <- 1:nrow(db) # If the user has previously set the cluster and does not wish to reset it if(!is.numeric(nproc)){ cluster <- nproc nproc <- 0 } # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc > 1) { cluster <- parallel::makeCluster(nproc, type = "PSOCK") parallel::clusterExport(cluster, list('db', 'sequenceColumn', 'germlineColumn', 'regionDefinition', 'regionDefinitionName', 'frequency', 'combine', 'ambiguousMode', 'calcObservedMutations','s2c','c2s','NUCLEOTIDES', 'NUCLEOTIDES_AMBIGUOUS', 'IUPAC2nucs', 'EXPANDED_AMBIGUOUS_CODONS', 'makeNullRegionDefinition', 'mutationDefinition', 'getCodonPos','getContextInCodon','mutationType', 'AMINO_ACIDS', 'binMutationsByRegion', 'countNonNByRegion','setRegionBoundaries','IMGT_V_BY_REGIONS'), envir=environment()) registerDoParallel(cluster,cores=nproc) } else if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } # Printing status to console #cat("Calculating observed number of mutations...\n") # Identify all the mutations in the sequences numbOfSeqs <- nrow(db) observedMutations_list <- foreach(idx=iterators::icount(numbOfSeqs)) %dopar% { rd <- regionDefinition if (regionDefinitionName %in% c("IMGT_VDJ_BY_REGIONS","IMGT_VDJ")) { rd <- setRegionBoundaries(juncLength = db[[juncLengthColumn]][idx], sequenceImgt = db[[sequenceColumn]][idx], regionDefinition=regionDefinition) } oM <- calcObservedMutations(db[[sequenceColumn]][idx], db[[germlineColumn]][idx], frequency=frequency & !combine, regionDefinition=rd, mutationDefinition=mutationDefinition, returnRaw=combine, ambiguousMode=ambiguousMode) this_row_id <- db[['tmp_obsmu_row_id']][idx] if (combine) { num_mutations <- 0 if (!all(is.na(oM$pos))) { num_mutations <- sum(oM$pos$r, oM$pos$s) } if (!frequency) { c("mu_count"=num_mutations, "tmp_obsmu_row_id"=this_row_id) } else { num_nonN <- sum(oM$nonN) mu_freq <- num_mutations/num_nonN c("mu_freq"=mu_freq, "tmp_obsmu_row_id"=this_row_id) } } else { oM['tmp_obsmu_row_id'] <- this_row_id oM } } # Convert list of mutations to data.frame if (combine) { labels_length <- 2 # mutation count and tmp_obsmu_row_id } else if (!is.null(regionDefinition)) { labels_length <- length(regionDefinition@labels) + 1 # +1 for tmp_obsmu_row_id } else{ #labels_length=1 labels_length <- length(makeNullRegionDefinition()@labels) +1 # +1 for tmp_obsmu_row_id } # Convert mutation vector list to a matrix observed_mutations <- as.data.frame(do.call(rbind, lapply(observedMutations_list, function(x) { length(x) <- labels_length return(x) })), stringsAsFactors=F) #observed_mutations <- t(sapply(observedMutations_list, c)) sep <- "_" if (ncol(observed_mutations) > 2) sep <- "_" observed_mutations[is.na(observed_mutations)] <- 0 col_names <- colnames(observed_mutations) mu_col_names <- col_names != "tmp_obsmu_row_id" if (frequency == TRUE) { idx <- which(colnames(observed_mutations)[mu_col_names] != "mu_freq") if (length(idx)>0){ colnames(observed_mutations)[mu_col_names][idx] <- gsub("_$","",paste("mu_freq", col_names[mu_col_names][idx], sep=sep)) } } else { idx <- which(colnames(observed_mutations)[mu_col_names] != "mu_count") if (length(idx)>0) { colnames(observed_mutations)[mu_col_names] <- gsub("_$","",paste("mu_count", col_names[mu_col_names][idx], sep=sep)) } } # Properly shutting down the cluster if (nproc > 1) { parallel::stopCluster(cluster) } # Bind the observed mutations to db db_new <- db %>% ungroup() %>% left_join(observed_mutations, by="tmp_obsmu_row_id") %>% arrange(!!rlang::sym("tmp_obsmu_row_id")) %>% select(-!!rlang::sym("tmp_obsmu_row_id")) return(db_new) } #' Count the number of observed mutations in a sequence. #' #' \code{calcObservedMutations} determines all the mutations in a given input sequence #' compared to its germline sequence. #' #' @param inputSeq input sequence. IUPAC ambiguous characters for DNA are #' supported. #' @param germlineSeq germline sequence. IUPAC ambiguous characters for DNA #' are supported. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. Note, only the part of #' sequences defined in \code{regionDefinition} are analyzed. #' If NULL, mutations are counted for entire sequence. #' @param mutationDefinition \link{MutationDefinition} object defining replacement #' and silent mutation criteria. If \code{NULL} then #' replacement and silent are determined by exact #' amino acid identity. #' @param ambiguousMode whether to consider ambiguous characters as #' \code{"either or"} or \code{"and"} when determining and #' counting the type(s) of mutations. Applicable only if #' \code{inputSeq} and/or \code{germlineSeq} #' contain(s) ambiguous characters. One of #' \code{c("eitherOr", "and")}. Default is \code{"eitherOr"}. #' @param returnRaw return the positions of point mutations and their #' corresponding mutation types, as opposed to counts of #' mutations across positions. Also returns the number of #' bases used as the denominator when calculating frequency. #' Default is \code{FALSE}. #' @param frequency \code{logical} indicating whether or not to calculate #' mutation frequencies. The denominator used is the number #' of bases that are not one of "N", "-", or "." in either #' the input or the germline sequences. If set, this #' overwrites \code{returnRaw}. Default is \code{FALSE}. #' #' @return For \code{returnRaw=FALSE}, an \code{array} with the numbers of replacement (R) #' and silent (S) mutations. #' #' For \code{returnRaw=TRUE}, a list containing #' \itemize{ #' \item \code{$pos}: A data frame whose columns (\code{position}, \code{r}, #' \code{s}, and \code{region}) indicate, respecitively, the nucleotide #' position, the number of R mutations at that position, the number of S #' mutations at that position, and the region in which that nucleotide #' is in. #' \item \code{$nonN}: A vector indicating the number of bases in regions #' defined by \code{regionDefinition} (excluding non-triplet overhang, #' if any) that are not one of "N", "-", or "." in either the #' \code{inputSeq} or \code{germlineSeq}. #' } #' #' For \code{frequency=TRUE}, regardless of \code{returnRaw}, an \code{array} #' with the frequencies of replacement (R) and silent (S) mutations. #' #' @details #' \strong{Each mutation is considered independently in the germline context}. For illustration, #' consider the case where the germline is \code{TGG} and the observed is \code{TAC}. #' When determining the mutation type at position 2, which sees a change from \code{G} to #' \code{A}, we compare the codon \code{TGG} (germline) to \code{TAG} (mutation at position #' 2 independent of other mutations in the germline context). Similarly, when determining #' the mutation type at position 3, which sees a change from \code{G} to \code{C}, we #' compare the codon \code{TGG} (germline) to \code{TGC} (mutation at position 3 independent #' of other mutations in the germline context). #' #' If specified, only the part of \code{inputSeq} defined in \code{regionDefinition} is #' analyzed. For example, when using the default \link{IMGT_V} definition, then mutations #' in positions beyond 312 will be ignored. Additionally, non-triplet overhang at the #' sequence end is ignored. #' #' Only replacement (R) and silent (S) mutations are included in the results. \strong{Excluded} #' are: #' \itemize{ #' \item Stop mutations #' #' E.g.: the case where \code{TAGTGG} is observed for the germline \code{TGGTGG}. #' #' \item Mutations occurring in codons where one or both of the observed and the #' germline involve(s) one or more of "N", "-", or ".". #' #' E.g.: the case where \code{TTG} is observed for the germline being any one of #' \code{TNG}, \code{.TG}, or \code{-TG}. Similarly, the case where any one of #' \code{TTN}, \code{TT.}, or \code{TT-} is observed for the germline \code{TTG}. #' #' } #' In other words, a result that is \code{NA} or zero indicates absence of R and S mutations, #' not necessarily all types of mutations, such as the excluded ones mentioned above. #' #' \code{NA} is also returned if \code{inputSeq} or \code{germlineSeq} is shorter than 3 #' nucleotides. #' #' @section Ambiguous characters: #' When there are ambiguous characters present, the user could choose how mutations involving #' ambiguous characters are counted through \code{ambiguousMode}. The two available modes #' are \code{"eitherOr"} and \code{"and"}. #' \itemize{ #' \item With \code{"eitherOr"}, ambiguous characters are each expanded but only #' 1 mutation is recorded. When determining the type of mutation, the #' priority for different types of mutations, in decreasing order, is as follows: #' no mutation, replacement mutation, silent mutation, and stop mutation. #' #' When counting the number of non-N, non-dash, and non-dot positions, each #' position is counted only once, regardless of the presence of ambiguous #' characters. #' #' As an example, consider the case where \code{germlineSeq} is \code{"TST"} and #' \code{inputSeq} is \code{"THT"}. Expanding \code{"H"} at position 2 in #' \code{inputSeq} into \code{"A"}, \code{"C"}, and \code{"T"}, as well as #' expanding \code{"S"} at position 2 in \code{germlineSeq} into \code{"C"} and #' \code{"G"}, one gets: #' #' \itemize{ #' \item \code{"TCT"} (germline) to \code{"TAT"} (observed): replacement #' \item \code{"TCT"} (germline) to \code{"TCT"} (observed): no mutation #' \item \code{"TCT"} (germline) to \code{"TTT"} (observed): replacement #' \item \code{"TGT"} (germline) to \code{"TAT"} (observed): replacement #' \item \code{"TGT"} (germline) to \code{"TCT"} (observed): replacement #' \item \code{"TGT"} (germline) to \code{"TTT"} (observed): replacement #' } #' #' Because "no mutation" takes priority over replacement mutation, the final #' mutation count returned for this example is \code{NA} (recall that only R and #' S mutations are returned). The number of non-N, non-dash, and non-dot #' positions is 3. #' #' \item With \code{"and"}, ambiguous characters are each expanded and mutation(s) #' from all expansions are recorded. #' #' When counting the number of non-N, non-dash, and non-dot positions, if a #' position contains ambiguous character(s) in \code{inputSeq} and/or #' \code{germlineSeq}, the count at that position is taken to be the total #' number of combinations of germline and observed codons after expansion. #' #' Using the same example from above, the final result returned for this example #' is that there are 5 R mutations at position 2. The number of non-N, non-dash, #' and non-dot positions is 8, since there are 6 combinations stemming from #' position 2 after expanding the germline codon (\code{"TST"}) and the observed #' codon (\code{"THT"}). #' } #' #' @seealso See \link{observedMutations} for counting the number of observed mutations #' in a \code{data.frame}. #' #' @examples #' # Use an entry in the example data for input and germline sequence #' data(ExampleDb, package="alakazam") #' in_seq <- ExampleDb[["sequence_alignment"]][100] #' germ_seq <- ExampleDb[["germline_alignment_d_mask"]][100] #' #' # Identify all mutations in the sequence #' ex1_raw <- calcObservedMutations(in_seq, germ_seq, returnRaw=TRUE) #' # Count all mutations in the sequence #' ex1_count <- calcObservedMutations(in_seq, germ_seq, returnRaw=FALSE) #' ex1_freq <- calcObservedMutations(in_seq, germ_seq, returnRaw=FALSE, frequency=TRUE) #' # Compare this with ex1_count #' table(ex1_raw$pos$region, ex1_raw$pos$r)[, "1"] #' table(ex1_raw$pos$region, ex1_raw$pos$s)[, "1"] #' # Compare this with ex1_freq #' table(ex1_raw$pos$region, ex1_raw$pos$r)[, "1"]/ex1_raw$nonN #' table(ex1_raw$pos$region, ex1_raw$pos$s)[, "1"]/ex1_raw$nonN #' #' # Identify only mutations the V segment minus CDR3 #' ex2_raw <- calcObservedMutations(in_seq, germ_seq, #' regionDefinition=IMGT_V, returnRaw=TRUE) #' # Count only mutations the V segment minus CDR3 #' ex2_count <- calcObservedMutations(in_seq, germ_seq, #' regionDefinition=IMGT_V, returnRaw=FALSE) #' ex2_freq <- calcObservedMutations(in_seq, germ_seq, #' regionDefinition=IMGT_V, returnRaw=FALSE, #' frequency=TRUE) #' # Compare this with ex2_count #' table(ex2_raw$pos$region, ex2_raw$pos$r)[, "1"] #' table(ex2_raw$pos$region, ex2_raw$pos$s)[, "1"] #' # Compare this with ex2_freq #' table(ex2_raw$pos$region, ex2_raw$pos$r)[, "1"]/ex2_raw$nonN #' table(ex2_raw$pos$region, ex2_raw$pos$s)[, "1"]/ex2_raw$nonN #' #' # Identify mutations by change in hydropathy class #' ex3_raw <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, #' mutationDefinition=HYDROPATHY_MUTATIONS, #' returnRaw=TRUE) #' # Count mutations by change in hydropathy class #' ex3_count <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, #' mutationDefinition=HYDROPATHY_MUTATIONS, #' returnRaw=FALSE) #' ex3_freq <- calcObservedMutations(in_seq, germ_seq, regionDefinition=IMGT_V, #' mutationDefinition=HYDROPATHY_MUTATIONS, #' returnRaw=FALSE, frequency=TRUE) #' # Compre this with ex3_count #' table(ex3_raw$pos$region, ex3_raw$pos$r)[, "1"] #' table(ex3_raw$pos$region, ex3_raw$pos$s)[, "1"] #' # Compare this with ex3_freq #' table(ex3_raw$pos$region, ex3_raw$pos$r)[, "1"]/ex3_raw$nonN #' table(ex3_raw$pos$region, ex3_raw$pos$s)[, "1"]/ex3_raw$nonN #' #' @export calcObservedMutations <- function(inputSeq, germlineSeq, regionDefinition=NULL, mutationDefinition=NULL, ambiguousMode=c("eitherOr", "and"), returnRaw=FALSE, frequency=FALSE) { ambiguousMode <- match.arg(ambiguousMode) if (is.na(inputSeq)) { inputSeq <- "" } if (is.na(germlineSeq)) { inputSeq <- "" } # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Check mutation definition if (!is.null(mutationDefinition) & !is(mutationDefinition, "MutationDefinition")) { stop(deparse(substitute(mutationDefinition)), " is not a valid MutationDefinition object") } # IMPORTANT: convert to uppercase # NUCLEOTIDES, NUCLEOTIDES_AMBIGUOUS are in uppercases only inputSeq <- toupper(inputSeq) germlineSeq <- toupper(germlineSeq) # Assign mutation definition aminoAcidClasses <- if (is.null(mutationDefinition)) { NULL } else { mutationDefinition@classes } # Removing IMGT gaps (they should come in threes) # After converting ... to ZZZ any other . is not an IMGT gap & will be treated like N germlineSeq <- gsub("\\.\\.\\.", "ZZZ", germlineSeq) #If there is a single gap left convert it to an N germlineSeq <- gsub("\\.", "N", germlineSeq) # Re-assigning s_germlineSeq (now has all "." that are not IMGT gaps converted to Ns) germlineSeq <- gsub("ZZZ", "...", germlineSeq) # Removing IMGT gaps (they should come in threes) # After converting ... to ZZZ any other . is not an IMGT gap & will be treated like N inputSeq <- gsub("\\.\\.\\.", "ZZZ", inputSeq) #If there is a single gap left convert it to an N inputSeq <- gsub("\\.", "N", inputSeq) # Re-assigning s_germlineSeq (now has all "." that are not IMGT gaps converted to Ns) inputSeq <- gsub("ZZZ", "...", inputSeq) # Trim the input and germline sequence to the shortest len_inputSeq <- stri_length(inputSeq) len_germlineSeq <- stri_length(germlineSeq) # If a regionDefinition is passed, # then only analyze till the end of the defined length if(!is.null(regionDefinition)) { rdLength <- regionDefinition@seqLength } else { rdLength <- max(len_inputSeq, len_germlineSeq, na.rm=TRUE) # Create full sequence RegionDefinition object regionDefinition <- makeNullRegionDefinition(rdLength) } len_shortest <- min(c(len_inputSeq, len_germlineSeq, rdLength), na.rm=TRUE) c_inputSeq <- s2c(inputSeq)[1:len_shortest] c_germlineSeq <- s2c(germlineSeq)[1:len_shortest] # If the sequence and germline (which now should be the same length) is shorter # than the rdLength, pad it with Ns if(len_shortest 0) { # The nucleotide positions of the mutations mutations_pos <- which(mutations==TRUE) # For every mutations_pos, extract the entire codon from germline mutations_pos_codons <- array(sapply(mutations_pos, getCodonPos)) c_germlineSeq_codons <- c_germlineSeq[mutations_pos_codons] # For every mutations_pos, extract the codon from input (without other mutations # at the same codon, if any). c_inputSeq_codons <- array(sapply(mutations_pos, function(x) { seqP <- c_germlineSeq[getCodonPos(x)] seqP[getContextInCodon(x)] <- c_inputSeq[x] return(seqP) })) # split the string of codons into vector of codons # [[:alnum:]]{3} will fail to capture non-ATGC (such as "-CC") # to include a literal -, place it first or last c_germlineSeq_codons <- strsplit(gsub("([A-Z\\.-]{3})", "\\1 ", c2s(c_germlineSeq_codons)), " ")[[1]] c_inputSeq_codons <- strsplit(gsub("([A-Z\\.-]{3})", "\\1 ", c2s(c_inputSeq_codons)), " ")[[1]] # Determine whether the mutations are R or S # a table where rows are r/s/stop/na, cols are codon positions # Count ambiguous characters as "eithe-or" or "and" based on user setting # Makes use of the fact that c_germlineSeq_codons and c_inputSeqCodons have # the same length mutations_array_raw <- sapply(1:length(c_germlineSeq_codons), function(i){ mutationType(codonFrom=c_germlineSeq_codons[i], codonTo=c_inputSeq_codons[i], ambiguousMode=ambiguousMode, aminoAcidClasses) }) # check dimension before assigning nucleotide positions to colnames stopifnot(ncol(mutations_array_raw)==length(mutations_pos)) colnames(mutations_array_raw) <- mutations_pos # keep only columns in which there are R or S mutations; and keep only R and S rows # use drop=FALSE so that matrix won't be collapsed into a vector if there is only 1 TRUE in keep.idx keep.idx <- apply(mutations_array_raw, 2, function(x) { any(x[c("r", "s")]>0) } ) keep.pos <- colnames(mutations_array_raw)[keep.idx] mutations_array_raw <- mutations_array_raw[c("r", "s"), keep.idx, drop=FALSE] colnames(mutations_array_raw) <- keep.pos # if none of columns have R or S > 1, dim will be 2x0 if ( ncol(mutations_array_raw)==0 ) { # NA if mutations_array_raw contains all NAs and they have all been removed mutations_array_raw <- NA mutations_array <- setNames(object=rep(NA, length(regionDefinition@labels)), nm=regionDefinition@labels) } else { # count each mutation type by region mutations_array <- binMutationsByRegion(mutations_array_raw, regionDefinition) } } } # frequency=TRUE overrides returnRaw=FALSE/TRUE if (frequency) { # avoid is.na(mutations_array_raw) to avoid warning in case mutations_array_raw is a vector if (length(mutations_array_raw) == sum(is.na(mutations_array_raw))) { return(mutations_array) } else { # Freq = numb of mutations / numb of non N bases (in both seq and gl) denoms <- countNonNByRegion(regDef=regionDefinition, ambiMode=ambiguousMode, inputChars=c_inputSeq, germChars=c_germlineSeq, inputCodons=c_inputSeq_codons, germCodons=c_germlineSeq_codons, mutPos=mutations_pos) mutations_array <- mutations_array/rep(denoms, each=2) return(mutations_array) } } # return positions of point mutations and their mutation types ("raw") if (returnRaw){ if (length(mutations_array_raw) == sum(is.na(mutations_array_raw))) { # if mutations_array_raw is NA, or # if mutations_array_raw is empty due to all mutations being "stop" and hence removed # avoid is.na(mutations_array_raw) to avoid warning in case mutations_array_raw is a vector if (!tooShort) { # when input and germline are >=3 nucleotides but there's no mutation # c_inputSeq_codons, c_germlineSeq_codons, and mutations_pos won't exist # this won't be a problem if ambiguousMode="eitherOr", but would for "and" # set inputCodons, germCodons, and mutPos to NULL to work around that nonN.denoms <- countNonNByRegion(regDef=regionDefinition, ambiMode=ambiguousMode, inputChars=c_inputSeq, germChars=c_germlineSeq, inputCodons=NULL, germCodons=NULL, mutPos=NULL) } else { nonN.denoms <- setNames(object=rep(NA, length(regionDefinition@regions)), nm=regionDefinition@regions) } return(list(pos=mutations_array_raw, nonN=nonN.denoms)) } else { nonN.denoms <- countNonNByRegion(regDef=regionDefinition, ambiMode=ambiguousMode, inputChars=c_inputSeq, germChars=c_germlineSeq, inputCodons=c_inputSeq_codons, germCodons=c_germlineSeq_codons, mutPos=mutations_pos) # df indicating position, mutation type (R or S), and region of each mutation rawDf <- data.frame(as.numeric(colnames(mutations_array_raw))) rawDf <- cbind(rawDf, mutations_array_raw["r", ], mutations_array_raw["s", ], as.character(regionDefinition@boundaries[as.numeric(colnames(mutations_array_raw))]), stringsAsFactors=F) colnames(rawDf) <- c("position", "r", "s", "region") return(list(pos=rawDf, nonN=nonN.denoms)) } } else { # return counts of each mutation type return(mutations_array) } } # Aggregate mutations by region # # \code{binMutationsByRegion} takes an array of observed mutations (e.g. from # \code{calcObservedMutations}) and bins them by the different regions defined in the # \code{regionDefinition}. # # @param mutationsArray \code{array} containing the number of R and S mutations # at the nucleotide positions where there are mutations. # @param regionDefinition \link{RegionDefinition} object defining the regions # and boundaries of the Ig sequences. # # @return An \code{array} of R/S mutations binned across all the unique regions defined # by \code{regionDefinition}. # # @details # Note, only the part of sequences defined in \code{regionDefinition} are analyzed. # For example, if the default \link{IMGT_V} definition is used, then mutations # in positions beyond 312 will be ignored. # # @seealso # See \link{observedMutations} for identifying and counting the # number of observed mutations. # This function is also used in \link{calcObservedMutations}. # # @examples # # Generate a random mutation array # numbOfMutPos <- sample(3:10, 1) # posOfMutations <- sort(sample(330, numbOfMutPos)) # mutations_array <- matrix(0, nrow=2, ncol=numbOfMutPos, dimnames=list(c("R", "S"), posOfMutations)) # mutations_array["r", ] = sample(x=0:10, size=numbOfMutPos, replace=TRUE) # mutations_array["s", ] = sample(x=0:10, size=numbOfMutPos, replace=TRUE) # # Random mutations # binMutationsByRegion(mutations_array, regionDefinition=NULL) # binMutationsByRegion(mutations_array, regionDefinition=IMGT_V) binMutationsByRegion <- function(mutationsArray, regionDefinition=NULL) { # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Create full sequence RegionDefinition object # The seqLength will be the largest index of a mutation if (is.null(regionDefinition)) { regionDefinition <- makeNullRegionDefinition(max(as.numeric(colnames(mutationsArray)))) } # get 2 vectors, 1 for R, 1 for S, along length of 1:regionDefinition@seqLength # each vector records the number of R/S at each position mutatedPositions <- as.numeric(colnames(mutationsArray)) mutations_R <- array(NA, dim=regionDefinition@seqLength) mutations_S <- array(NA, dim=regionDefinition@seqLength) mutations_R[mutatedPositions] <- mutationsArray["r", ] mutations_S[mutatedPositions] <- mutationsArray["s", ] mutations_R <- mutations_R[1:regionDefinition@seqLength] mutations_S <- mutations_S[1:regionDefinition@seqLength] # count number of R/S in each region mutations_region_counts <- rep(0, length(regionDefinition@labels)) names(mutations_region_counts) <- regionDefinition@labels for (reg in regionDefinition@regions) { mutations_region_counts[paste0(reg, "_r")] <- sum(mutations_R[regionDefinition@boundaries==reg], na.rm=T) mutations_region_counts[paste0(reg, "_s")] <- sum(mutations_S[regionDefinition@boundaries==reg], na.rm=T) } return(mutations_region_counts) } # Count the number of non-N, non-dash, and non-dot positions # # @param regDef regionDefinition # @param ambiMode ambiguousMode # @param inputChars c_inputSeq # @param germChars c_germlineSeq # @param inputCodons c_inputSeq_codons # @param germCodons c_germlineSeq_codons # @param mutPos mutations_pos # # @return The number of non-N, non-dash, and non-dot characters. Calculation method # differs depending on ambiMode being "eitherOr" or "and". By design, when # there is no ambiguous character in the input or germline, the result should be # the same regardless of ambiMode. # # @details This is a helper function for calcObservedMutations() and is not intended to # be called directly. All input arguments are, by design, expected to be # generated as intermediate products during a call to calcObservedMutations(). # countNonNByRegion <- function(regDef, ambiMode, inputChars, germChars, inputCodons, germCodons, mutPos) { regionNames <- unique(sapply(regDef@labels, function(x) { substr(x, 1, stri_length(x)-2) })) if (ambiMode=="eitherOr") { # Subset boundaries to only non-N & non-dash & non-dot bases (in both seq and gl) # "which" in next line is ESSENTIAL; otherwise @boundaries won't be truncated # e.g. (1:6)[c(T,T,T)] returns 1:6, not 1:3 boundaries <- regDef@boundaries[ which(inputChars %in% NUCLEOTIDES_AMBIGUOUS[1:14] & germChars %in% NUCLEOTIDES_AMBIGUOUS[1:14])] # number of non-N & non-dash & non-dot bases (in both seq and gl) nonN <- sapply(regionNames, function(x) { sum(boundaries==x) }) } else if (ambiMode=="and") { ### positions where there's no mutation: # simply count the positions where both input and germline are # non-N, non-dash, and non-dot boundaries.1 <- regDef@boundaries[ which(inputChars %in% NUCLEOTIDES_AMBIGUOUS[1:14] & germChars %in% NUCLEOTIDES_AMBIGUOUS[1:14] & (germChars == inputChars))] nonN.1 <- sapply(regionNames, function(x) { sum(boundaries.1==x) }) ### positions where there's mutation: if ( (!is.null(inputCodons)) & (!is.null(germCodons)) & (!is.null(mutPos)) ) { # expand codon with ambiguous character(s) into codons with unambiguous characters # calculate the number of possible combinations between input and germline codons # this makes use of the important fact that each mutation is considered # independently in the germline context inputNumExpanded <- sapply(inputCodons, function(codon){ length(EXPANDED_AMBIGUOUS_CODONS[[codon]]) }) germlineNumExpanded <- sapply(germCodons, function(codon){ length(EXPANDED_AMBIGUOUS_CODONS[[codon]]) }) totalNumExpanded <- inputNumExpanded * germlineNumExpanded # use mutations_pos to capture positions at which r/s is absent (stop or na instead) # such positions would have been omitted from mutations_array_raw or mutations_array boundaries.2 <- regDef@boundaries[mutPos] # makes use of the fact that inputCodons, germCodons, and # mutPos align exactly nonN.2 <- sapply(regionNames, function(x){ sum(totalNumExpanded[boundaries.2==x]) }) } else { nonN.2 <- setNames(object=rep(0, length(regionNames)), nm=regionNames) } nonN <- nonN.1 + nonN.2 } return(nonN) } #### Sliding window approach #### #' Sliding window approach towards filtering a single sequence #' #' \code{slideWindowSeq} determines whether an input sequence contains equal to or more than #' a given number of mutations in a given length of consecutive nucleotides (a "window") #' when compared to a germline sequence. #' #' @param inputSeq input sequence. #' @param germlineSeq germline sequence. #' @param mutThresh threshold on the number of mutations in \code{windowSize} #' consecutive nucleotides. Must be between 1 and \code{windowSize} #' inclusive. #' @param windowSize length of consecutive nucleotides. Must be at least 2. #' #' @return \code{TRUE} if there are equal to or more than \code{mutThresh} number of mutations #' in any window of \code{windowSize} consecutive nucleotides (i.e. the sequence should #' be filtered); \code{FALSE} if otherwise. #' #' @seealso \link{calcObservedMutations} is called by \code{slideWindowSeq} to identify observed #' mutations. See \link{slideWindowDb} for applying the sliding window approach on a #' \code{data.frame}. See \link{slideWindowTune} for parameter tuning for \code{mutThresh} #' and \code{windowSize}. #' #' @examples #' # Use an entry in the example data for input and germline sequence #' data(ExampleDb, package="alakazam") #' in_seq <- ExampleDb[["sequence_alignment"]][100] #' germ_seq <- ExampleDb[["germline_alignment_d_mask"]][100] #' #' # Determine if in_seq has 6 or more mutations in 10 consecutive nucleotides #' slideWindowSeq(inputSeq=in_seq, germlineSeq=germ_seq, mutThresh=6, windowSize=10) #' slideWindowSeq(inputSeq="TCGTCGAAAA", germlineSeq="AAAAAAAAAA", mutThresh=6, windowSize=10) #' @export slideWindowSeq <- function(inputSeq, germlineSeq, mutThresh, windowSize){ # identify all R and S mutations in input sequence inputMut <- calcObservedMutations(inputSeq=inputSeq, germlineSeq=germlineSeq, returnRaw=T) # call helper return(slideWindowSeqHelper(mutPos=inputMut$pos, mutThresh=mutThresh, windowSize=windowSize)) } # NOTE: DO NOT MERGE slideWindowSeqHelper with slideWindowSeq (very different input formats) # slideWindowTune needs to call slideWindowSeqHelper directly for efficiency # Helper for sliding window approach towards filtering sequences # # @param mutPos a \code{data.frame} containing positions and types of point # mutations as returned in \code{$pos} by # \code{calcObserverdMutations()} with \code{returnRaw=TRUE}. # Can be \code{NA}, in which case the returned value will be # \code{FALSE}. # @param mutThresh threshold on the number of mutations in \code{windowSize} # consecutive nucleotides. Must be between 1 and \code{windowSize} # inclusive. # @param windowSize length of consecutive nucleotides. Must be at least 2. # # @return \code{TRUE} if there are equal to or more than \code{mutThresh} number of mutations # in any window of \code{windowSize} consecutive nucleotides; \code{FALSE} if otherwise. slideWindowSeqHelper <- function(mutPos, mutThresh, windowSize){ # check preconditions stopifnot(mutThresh >= 1 & mutThresh <= windowSize & windowSize>=2) if (length(mutPos) == 1 && is.na(mutPos)) { # use && instead of & to short-circuit in case length(mutPos)!=1 (otherwise warning) return(FALSE) } else { # general idea: # only need to check windows containing mutations (as opposed to every possible window) for (i in 1:nrow(mutPos)){ # get window limits lower <- mutPos$position[i] upper <- lower + windowSize - 1 # how many mutations fall within current window windowCount <- sum(mutPos[mutPos$position>=lower & mutPos$position<=upper, c("r","s")]) # return as soon as a window has >= mutThresh mutations if (windowCount >= mutThresh) { return(TRUE) } } return(FALSE) } } #' Sliding window approach towards filtering sequences in a \code{data.frame} #' #' \code{slideWindowDb} determines whether each input sequence in a \code{data.frame} #' contains equal to or more than a given number of mutations in a given length of #' consecutive nucleotides (a "window") when compared to their respective germline #' sequence. #' #' @param db \code{data.frame} containing sequence data. #' @param sequenceColumn name of the column containing IMGT-gapped sample sequences. #' @param germlineColumn name of the column containing IMGT-gapped germline sequences. #' @param mutThresh threshold on the number of mutations in \code{windowSize} #' consecutive nucleotides. Must be between 1 and \code{windowSize} #' inclusive. #' @param windowSize length of consecutive nucleotides. Must be at least 2. #' @param nproc Number of cores to distribute the operation over. If the #' \code{cluster} has already been set earlier, then pass the #' \code{cluster}. This will ensure that it is not reset. #' #' @return a logical vector. The length of the vector matches the number of input sequences in #' \code{db}. Each entry in the vector indicates whether the corresponding input sequence #' should be filtered based on the given parameters. #' #' @seealso See \link{slideWindowSeq} for applying the sliding window approach on a single sequence. #' See \link{slideWindowTune} for parameter tuning for \code{mutThresh} and \code{windowSize}. #' #' @examples #' # Use an entry in the example data for input and germline sequence #' data(ExampleDb, package="alakazam") #' #' # Apply the sliding window approach on a subset of ExampleDb #' slideWindowDb(db=ExampleDb[1:10, ], sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' mutThresh=6, windowSize=10, nproc=1) #' #' @export slideWindowDb <- function(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", mutThresh=6, windowSize=10, nproc=1){ # Hack for visibility of foreach index variables i <- NULL # Check input check <- checkColumns(db, c(sequenceColumn, germlineColumn)) if (check != TRUE) { stop(check) } db <- db[,c(sequenceColumn, germlineColumn)] # If the user has previously set the cluster and does not wish to reset it if(!is.numeric(nproc)){ stop_cluster <- FALSE cluster <- nproc nproc <- 0 } else { stop_cluster <- TRUE } # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc == 1) { # If needed to run on a single core/cpu then, register DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } else { cluster_type <- "FORK" if (.Platform$OS.type == "windows") { cluster_type <- "PSOCK" } if (nproc != 0) { #cluster <- makeCluster(nproc, type="SOCK") cluster <- parallel::makeCluster(nproc, type= cluster_type) } parallel::clusterExport(cluster, list('db', 'sequenceColumn', 'germlineColumn', 'mutThresh', 'windowSize','slideWindowSeq'), envir=environment() ) registerDoParallel(cluster,cores=nproc) } filter <- unlist(foreach(i=1:nrow(db), .verbose=FALSE, .errorhandling='stop') %dopar% { slideWindowSeq(inputSeq = db[i, sequenceColumn], germlineSeq = db[i, germlineColumn], mutThresh = mutThresh, windowSize = windowSize) }) if (stop_cluster & !is.numeric(nproc)) { parallel::stopCluster(cluster) } filter } #' Parameter tuning for sliding window approach #' #' Apply \link{slideWindowDb} over a search grid made of combinations of \code{mutThresh} and #' \code{windowSize} to help with picking a pair of values for these parameters. Parameter #' tuning can be performed by choosing a combination that gives a reasonable number of #' filtered/remaining sequences. #' #' @param db \code{data.frame} containing sequence data. #' @param sequenceColumn name of the column containing IMGT-gapped sample sequences. #' @param germlineColumn name of the column containing IMGT-gapped germline sequences. #' @param dbMutList if supplied, this should be a list consisting of \code{data.frame}s #' returned as \code{$pos} in the nested list produced by #' \link{calcObservedMutations} with \code{returnRaw=TRUE}; otherwise, #' \link{calcObservedMutations} is called on columns \code{sequenceColumn} #' and \code{germlineColumn} of \code{db}. Default is \code{NULL}. #' @param mutThreshRange range of threshold on the number of mutations in \code{windowSize} #' consecutive nucleotides to try. Must be between 1 and #' maximum \code{windowSizeRange} inclusive. #' @param windowSizeRange range of length of consecutive nucleotides to try. The lower end #' must be at least 2. #' @param verbose whether to print out messages indicating current progress. Default #' is \code{TRUE}. #' @param nproc Number of cores to distribute the operation over. If the #' \code{cluster} has already been set earlier, then pass the #' \code{cluster}. This will ensure that it is not reset. #' @return a list of logical matrices. Each matrix corresponds to a \code{windowSize} in #' \code{windowSizeRange}. Each column in a matrix corresponds to a \code{mutThresh} in #' \code{mutThreshRange}. Each row corresponds to a sequence. \code{TRUE} values #' mean the sequences has at least the number of mutations specified in the column name, #' for that \code{windowSize}. #' #' @details If, in a given combination of \code{mutThresh} and \code{windowSize}, \code{mutThresh} #' is greater than \code{windowSize}, \code{NA}s will be returned for that particular #' combination. A message indicating that the combination has been "skipped" will be #' printed if \code{verbose=TRUE}. #' #' If \link{calcObservedMutations} was previously run on \code{db} and saved, supplying #' \code{$pos} from the saved result as \code{dbMutList} could save time by skipping a #' second call of \link{calcObservedMutations}. This could be helpful especially when #' \code{db} is large. #' #' @seealso \link{slideWindowDb} is called on \code{db} for tuning. See \link{slideWindowTunePlot} #' for visualization. See \link{calcObservedMutations} for generating \code{dbMutList}. #' #' @examples #' # Load and subset example data #' data(ExampleDb, package="alakazam") #' db <- ExampleDb[1:5, ] #' #' # Try out thresholds of 2-4 mutations in window sizes of 7-9 nucleotides. #' # In this case, all combinations are legal. #' slideWindowTune(db, mutThreshRange=2:4, windowSizeRange=7:9) #' #' # Illegal combinations are skipped, returning NAs. #' slideWindowTune(db, mutThreshRange=2:4, windowSizeRange=2:4, #' verbose=FALSE) #' #' # Run calcObservedMutations separately #' exDbMutList <- sapply(1:5, function(i) { #' calcObservedMutations(inputSeq=db[["sequence_alignment"]][i], #' germlineSeq=db[["germline_alignment_d_mask"]][i], #' returnRaw=TRUE)$pos }) #' slideWindowTune(db, dbMutList=exDbMutList, #' mutThreshRange=2:4, windowSizeRange=2:4) #' @export slideWindowTune <- function(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", dbMutList=NULL, mutThreshRange, windowSizeRange, verbose=TRUE, nproc=1){ # Hack for visibility of foreach index variables i <- NULL # check preconditions stopifnot(!is.null(db)) stopifnot(min(mutThreshRange) >= 1 & max(mutThreshRange) <= max(windowSizeRange) & min(windowSizeRange) >= 2) db <- db[,c(sequenceColumn, germlineColumn)] # If the user has previously set the cluster and does not wish to reset it if(!is.numeric(nproc)){ stop_cluster <- FALSE cluster <- nproc nproc <- 0 } else { stop_cluster <- TRUE } # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount()) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } else { cluster_type <- "FORK" if (.Platform$OS.type == "windows") { cluster_type <- "PSOCK" } if (nproc != 0) { #cluster <- makeCluster(nproc, type="SOCK") cluster <- parallel::makeCluster(nproc, type= cluster_type) } parallel::clusterExport(cluster, list('db', 'sequenceColumn', 'germlineColumn', 'calcObservedMutations','slideWindowSeqHelper'), envir=environment() ) registerDoParallel(cluster,cores=nproc) } # get positions of R/S mutations for sequences in db # do this here and then call slideWindowSeqHelper (so it's done only once) # instead of calling slideWindowDb which does this every time it is called if (is.null(dbMutList)) { if (verbose) {cat(paste0("Identifying mutated positions\n"))} pb <- txtProgressBar(0, nrow(db), style = 3 ) inputMutList <- foreach(i=1:nrow(db), .verbose=FALSE, .errorhandling='stop') %dopar% { setTxtProgressBar(pb, i) calcObservedMutations(inputSeq=db[i, sequenceColumn], germlineSeq=db[i, germlineColumn], returnRaw=T)$pos } } else { if (verbose) {cat("dbMutList supplied; skipped calling calcObservedMutations()\n")} inputMutList <- dbMutList } # if (nproc != 1) { # parallel::clusterExport(cluster, # list('dbMutList'), # envir=environment() ) # } # Get window-threshold combinations combs <- expand.grid(windowSizeRange, mutThreshRange) pb2 <- txtProgressBar(0, nrow(combs), style = 3 ) if (verbose) {cat(paste0("\nAnalyzing combinations of windowSizeRange and mutThreshRange\n"))} tmp <- foreach(i=1:nrow(combs), .verbose=FALSE, .combine=rbind, .errorhandling='stop') %dopar% { setTxtProgressBar(pb2, i) size <- combs[i,1] thresh <- combs[i,2] if (thresh <= size){ # apply slideWindow using current pair of parameters cur.logical <- unlist(lapply(inputMutList, slideWindowSeqHelper, mutThresh = thresh, windowSize = size)) } else { if (verbose) {cat(paste0(">>> mutThresh = ", thresh, " > windowSize = ", size, " (skipped)\n"))} # NA if skipped cur.logical <- rep(NA, nrow(db)) } data.frame(list( "windowSize"=size, "mutThreshold"=thresh, "cur_logical"=cur.logical, "row_idx"=1:length(cur.logical) )) } cur.list <- lapply(split(tmp, f=tmp[['windowSize']]), function(x) { x <- x[,colnames(x) != "windowSize"] pivot_wider(x, names_from=!!rlang::sym("mutThreshold"), values_from=!!rlang::sym("cur_logical"), id_cols=!!rlang::sym("row_idx")) %>% arrange(!!rlang::sym("row_idx")) %>% select(-!!rlang::sym("row_idx")) %>% as.matrix() }) if (stop_cluster & !is.numeric(nproc)) { parallel::stopCluster(cluster) } return(cur.list) } #' Visualize parameter tuning for sliding window approach #' #' Visualize results from \link{slideWindowTune} #' #' @param tuneList a list of logical matrices returned by \link{slideWindowTune}. #' @param plotFiltered whether to plot the number of filtered ('filtered'), #' or remaining ('remaining') sequences for each mutation threshold. #' Use 'per_mutation' to plot the number of sequences at each mutation #' value. Default is \code{'filtered'}. #' @param percentage whether to plot on the y-axis the percentage of filtered sequences #' (as opposed to the absolute number). Default is \code{FALSE}. #' @param jitter.x whether to jitter x-axis values. Default is \code{FALSE}. #' @param jitter.x.amt amount of jittering to be applied on x-axis values if #' \code{jitter.x=TRUE}. Default is 0.1. #' @param jitter.y whether to jitter y-axis values. Default is \code{FALSE}. #' @param jitter.y.amt amount of jittering to be applied on y-axis values if #' \code{jitter.y=TRUE}. Default is 0.1. #' @param pchs point types to pass on to \link{plot}. Default is #' \code{1:length(tuneList)}. #' @param ltys line types to pass on to \link{plot}. Default is #' \code{1:length(tuneList)}. #' @param cols colors to pass on to \link{plot}. #' @param plotLegend whether to plot legend. Default is \code{TRUE}. #' @param legendPos position of legend to pass on to \link{legend}. Can be either a #' numeric vector specifying x-y coordinates, or one of #' \code{"topright"}, \code{"center"}, etc. Default is \code{"topright"}. #' @param legendHoriz whether to make legend horizontal. Default is \code{FALSE}. #' @param legendCex numeric values by which legend should be magnified relative to 1. #' @param title plot main title. Default is NULL (no title) #' @param returnRaw Return a data.frame with sequence counts (TRUE) or a #' plot. Default is \code{FALSE}. #' #' @details For each \code{windowSize}, if \code{plotFiltered='filtered'}, the x-axis #' represents a mutation threshold range, and the y-axis the number of #' sequences that have at least that number of mutations. If #' \code{plotFiltered='remaining'}, the y-axis represents the number of sequences #' that have less mutations than the mutation threshold range. For the same #' window size, a sequence can be included in the counts for different #' mutation thresholds. For example, sequence "CCACCAAAA" with germline #' "AAAAAAAAA" has 4 mutations. This sequence has at least 2 mutations #' and at least 3 mutations, in a window of size 4. the sequence will #' be included in the sequence count for mutation thresholds 2 and 3. #' If \code{plotFiltered='per_mutation'}, the sequences are counted only once #' for each window size, at their largest mutation threshold. The above #' example sequence would be included in the sequence count for #' mutation threshold 3. #' #' When plotting, a user-defined \code{amount} of jittering can be applied on values plotted #' on either axis or both axes via adjusting \code{jitter.x}, \code{jitter.y}, #' \code{jitter.x.amt} and \code{jitter.y.amt}. This may be help with visually distinguishing #' lines for different window sizes in case they are very close or identical to each other. #' If plotting percentages (\code{percentage=TRUE}) and using jittering on the y-axis values #' (\code{jitter.y=TRUE}), it is strongly recommended that \code{jitter.y.amt} be set very #' small (e.g. 0.01). #' #' \code{NA} for a combination of \code{mutThresh} and \code{windowSize} where #' \code{mutThresh} is greater than \code{windowSize} will not be plotted. #' #' @seealso See \link{slideWindowTune} for how to get \code{tuneList}. See \link{jitter} for #' use of \code{amount} of jittering. #' #' @examples #' # Use an entry in the example data for input and germline sequence #' data(ExampleDb, package="alakazam") #' #' # Try out thresholds of 2-4 mutations in window sizes of 3-5 nucleotides #' # on a subset of ExampleDb #' tuneList <- slideWindowTune(db = ExampleDb[1:10, ], #' mutThreshRange = 2:4, windowSizeRange = 3:5, #' verbose = FALSE) #' #' # Visualize #' # Plot numbers of sequences filtered without jittering y-axis values #' plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered='filtered', jitter.y=FALSE) #' #' # Notice that some of the lines overlap #' # Jittering could help #' plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered='filtered', jitter.y=TRUE) #' #' # Plot numbers of sequences remaining instead of filtered #' plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered='remaining', jitter.y=TRUE, #' legendPos="bottomright") #' #' # Plot percentages of sequences filtered with a tiny amount of jittering #' plotSlideWindowTune(tuneList, pchs=1:3, ltys=1:3, cols=1:3, #' plotFiltered='filtered', percentage=TRUE, #' jitter.y=TRUE, jitter.y.amt=0.01) #' @export plotSlideWindowTune <- function(tuneList, plotFiltered = c('filtered','remaining','per_mutation'), percentage = FALSE, jitter.x = FALSE, jitter.x.amt = 0.1, jitter.y = FALSE, jitter.y.amt = 0.1, pchs = 1:length(tuneList), ltys = 1:length(tuneList), cols = 1, plotLegend = TRUE, legendPos = "topright", legendHoriz = FALSE, legendCex = 1, title=NULL, returnRaw=FALSE){ # collapse parameter if no user input then first item is selected plotFiltered <- match.arg(plotFiltered) if (plotFiltered == 'filtered') { xlab <- "Threshold on number of mutations" ylab.part.2 <- "filtered" } else if (plotFiltered == 'remaining') { # invert (!) tuneList if plotting retained sequences tuneList <- lapply(tuneList, function(x){!x}) xlab <- "Threshold on number of mutations" ylab.part.2 <- "remaining" } else if (plotFiltered == 'per_mutation') { xlab <- "Maximum number of mutations" ylab.part.2 <- 'per_mutation' } else { warning("plotFiltered must be in [filtered, remaining, per_mutation].\n ", plotFiltered, " received instead. \n") } # if number of pchs/ltys/cols provided does not match number of lines expected # expand into vector with repeating values (otherwise legend would break) if (length(pchs)!=length(tuneList)) {pchs <- rep(pchs, length.out=length(tuneList))} if (length(ltys)!=length(tuneList)) {ltys <- rep(ltys, length.out=length(tuneList))} if (length(cols)!=length(tuneList)) {cols <- rep(cols, length.out=length(tuneList))} # tabulate tuneList (and if applicable convert to percentage) if (plotFiltered == 'per_mutation') { # preprocess tuneList to count each sequence once, # considering the largest number of mutations in the window plotList.tmp <- lapply(tuneList, function(window_df) { # For each sequence bind_rows(lapply(1:nrow(window_df), function(i) { x <- window_df[i,] # Find the mutation thresholds that are T idx <- which(x) # If there are none (all F or NA values) or there is only one, do nothig # If there are more than one, keep the largest index and set the previous values to F if (length(idx) > 1) { idx <- max(idx) x[1:(idx-1)] <- F } x })) }) tuneList <- plotList.tmp } plotList <- lapply(tuneList, colSums) if (percentage) {plotList <- lapply(plotList, function(x){x/nrow(tuneList[[1]])})} if (returnRaw) { return (bind_rows(plotList, .id = "windowSize")) } # get x-axis values (i.e. mutThreshRange; colnames of matrix in tuneList with most columns) #threshes = as.numeric(colnames(tuneList[[which.max(lapply(lapply(tuneList, colnames), length))]])) threshes <- as.numeric(colnames(tuneList[[1]])) # plot for first window size x1 <- threshes if (jitter.x) {x1 <- jitter(x1, amount=jitter.x.amt)} y1 <- plotList[[1]] if (jitter.y) {y1 <- jitter(y1, amount=jitter.y.amt)} if (percentage) { ylab.part.1 <- "Percentage of sequences" # ylim ylim.padding <- abs(diff(range(plotList, na.rm=T)))*0.01 ylims <- c(max(0, min(range(plotList, na.rm=T)) - ylim.padding), min(1, max(range(plotList, na.rm=T)) + ylim.padding) ) } else { ylab.part.1 <- "Number of sequences" # ylim: non-negative lower limit; upper limit slight above max tabulated sum ylims <- c( max(0, min(range(plotList, na.rm=T)) - max(1, jitter.y.amt) ), max(range(plotList, na.rm=T)) + max(1, jitter.y.amt) ) } plot(x1, # mutThreshRange on x-axis y1, # tabulated sums in plotList on y-axis ylim = ylims, # xlim: +/- jitter.x.amt*2 to accommodate for amount of jittering on x-axis xlim = c(min(threshes)-jitter.x.amt*2, max(threshes+jitter.x.amt*2)), xaxt="n", xlab=xlab, ylab=paste(ylab.part.1, ylab.part.2), cex.lab=1.5, cex.axis=1.5, type="b", lwd=1.5, pch=pchs[1], lty=ltys[1], col=cols[1]) axis(side=1, at=threshes, cex.axis=1.5) # add title if (!is.null(title)) { title(main=title) } # plot for the rest of the window sizes for (i in 1:length(plotList)){ if (i>=2) { xi <- threshes if (jitter.x) {xi <- jitter(xi, amount=jitter.x.amt)} yi <- plotList[[i]] if (jitter.y) {yi <- jitter(yi, amount=jitter.y.amt)} points(xi, yi, type='b', lwd=1.5, pch=pchs[i], lty=ltys[i], col=cols[i]) } } # add legend if (plotLegend) { # if legendPos specified as xy coordinates if (is.numeric(legendPos) & length(legendPos)==2) { legend(x=legendPos[1], y=legendPos[2], legend = c("Window Size", names(tuneList)), horiz = legendHoriz, cex = legendCex, pch=c(NA, pchs), lty=c(NA, ltys), col=c(NA, cols)) } else { # if legendPos specified as "center", "topright", etc. legend(legendPos, legend = c("Window Size", names(tuneList)), horiz = legendHoriz, cex = legendCex, pch=c(NA, pchs), lty=c(NA, ltys), col=c(NA, cols)) } } } #### Expected frequencies calculating functions #### #' Calculate expected mutation frequencies #' #' \code{expectedMutations} calculates the expected mutation frequencies for each #' sequence in the input \code{data.frame}. #' #' @param db \code{data.frame} containing sequence data. #' @param sequenceColumn \code{character} name of the column containing input #' sequences. #' @param germlineColumn \code{character} name of the column containing #' the germline or reference sequence. #' @param targetingModel \link{TargetingModel} object. Default is \link{HH_S5F}. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. To use regions definitions, #' sequences in \code{sequenceColum} and \code{germlineColumn} #' must be aligned, following the IMGT schema. #' @param mutationDefinition \link{MutationDefinition} object defining replacement #' and silent mutation criteria. If \code{NULL} then #' replacement and silent are determined by exact #' amino acid identity. #' @param nproc \code{numeric} number of cores to distribute the operation #' over. If the cluster has already been set the call function with #' \code{nproc} = 0 to not reset or reinitialize. Default is #' \code{nproc} = 1. #' @param cloneColumn clone id column name in \code{db} #' @param juncLengthColumn junction length column name in \code{db} #' #' @return A modified \code{db} \code{data.frame} with expected mutation frequencies #' for each region defined in \code{regionDefinition}. #' #' The columns names are dynamically created based on the regions in #' \code{regionDefinition}. For example, when using the \link{IMGT_V} #' definition, which defines positions for CDR and FWR, the following columns are #' added: #' \itemize{ #' \item \code{mu_expected_cdr_r}: number of replacement mutations in CDR1 and #' CDR2 of the V-segment. #' \item \code{mu_expected_cdr_s}: number of silent mutations in CDR1 and CDR2 #' of the V-segment. #' \item \code{mu_expected_fwr_r}: number of replacement mutations in FWR1, #' FWR2 and FWR3 of the V-segment. #' \item \code{mu_expected_fwr_s}: number of silent mutations in FWR1, FWR2 and #' FWR3 of the V-segment. #' } #' #' @details #' Only the part of the sequences defined in \code{regionDefinition} are analyzed. #' For example, when using the \link{IMGT_V} definition, mutations in #' positions beyond 312 will be ignored. #' #' @seealso #' \link{calcExpectedMutations} is called by this function to calculate the expected #' mutation frequencies. See \link{observedMutations} for getting observed #' mutation counts. See \link{IMGT_SCHEMES} for a set of predefined #' \link{RegionDefinition} objects. #' #' @examples #' # Subset example data #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d") #' set.seed(112) #' db <- dplyr::slice_sample(db, n=100) #' # Calculate expected mutations over V region #' db_exp <- expectedMutations(db, #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' regionDefinition=IMGT_V, #' nproc=1) #' #' # Calculate hydropathy expected mutations over V region #' db_exp <- expectedMutations(db, #' sequenceColumn="sequence_alignment", #' germlineColumn="germline_alignment_d_mask", #' regionDefinition=IMGT_V, #' mutationDefinition=HYDROPATHY_MUTATIONS, #' nproc=1) #' #' @export expectedMutations <- function(db,sequenceColumn = "sequence_alignment", germlineColumn = "germline_alignment", targetingModel = HH_S5F, regionDefinition=NULL, mutationDefinition = NULL, nproc = 1, cloneColumn = "clone_id", juncLengthColumn = "junction_length") { # Hack for visibility of foreach index variable idx <- NULL check <- checkColumns(db, c(sequenceColumn, germlineColumn)) if (check != TRUE) { stop(check) } regionDefinitionName <- "" if (!is.null(regionDefinition)) { regionDefinitionName <- regionDefinition@name # Message if sequences don't have gaps or Ns (because makeChangeo clone # masks IMGT gaps) as a proxy to detect not IMGT aligned sequences if (all(!grepl("[\\.Nn]",db[[sequenceColumn]]))) { warning("No IMGT gaps detected in ",sequenceColumn,".\nSequences in ", sequenceColumn," and ", germlineColumn, " should be aligned, with gaps (.,N or n) following the IMGT numbering scheme.") } if (all(!grepl("[\\.Nn]",db[[germlineColumn]]))) { warning("No IMGT gaps detected in ",germlineColumn, ".\nSequences in ", sequenceColumn," and ", germlineColumn, " should be aligned, with gaps (., N or n) following the IMGT numbering scheme.") } not_na <- !is.na(db[[germlineColumn]]) if (!all.equal(nchar(db[[sequenceColumn]][not_na]), nchar(db[[germlineColumn]][not_na]))) { warning("Pairs of ", sequenceColumn, " and ", germlineColumn, " sequences with different lengths found.") stop("Expecting IMGT aligned, same length sequences in ", sequenceColumn, " and ", germlineColumn,".") } } # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Check mutation definition if (!is.null(mutationDefinition) & !is(mutationDefinition, "MutationDefinition")) { stop(deparse(substitute(mutationDefinition)), " is not a valid MutationDefinition object") } # Check if mutation count/freq columns already exist # and throw overwritting warning if (!is.null(regionDefinition)) { labels <- regionDefinition@labels } else { labels <- makeNullRegionDefinition()@labels } labels <- paste("mu_expected_", labels, sep="") label_exists <- labels[labels %in% colnames(db)] if (length(label_exists)>0) { warning(paste0("Columns ", paste(label_exists, collapse=", "), " exist and will be overwritten") ) db[,label_exists] <- NULL } # Check targeting model if (!is(targetingModel, "TargetingModel")) { stop(deparse(substitute(targetingModel)), " is not a valid TargetingModel object") } db$tmp_expmu_row_id <- 1:nrow(db) # Convert sequence columns to uppercase db <- toupperColumns(db, c(sequenceColumn, germlineColumn)) # If the user has previously set the cluster and does not wish to reset it if(!is.numeric(nproc)){ cluster = nproc nproc = 0 } # Ensure that the nproc does not exceed the number of cores/CPUs available nproc <- min(nproc, cpuCount(), na.rm=T) # If user wants to paralellize this function and specifies nproc > 1, then # initialize and register slave R processes/clusters & # export all nesseary environment variables, functions and packages. if (nproc > 1) { cluster <- parallel::makeCluster(nproc, type = "PSOCK") parallel::clusterExport(cluster, list('db', 'sequenceColumn', 'germlineColumn', 'regionDefinitionName', 'juncLengthColumn', 'setRegionBoundaries', 'regionDefinition','targetingModel', 'calcExpectedMutations','calculateTargeting', 's2c','c2s','NUCLEOTIDES','HH_S5F', 'calculateMutationalPaths','CODON_TABLE','IMGT_V_BY_REGIONS'), envir=environment() ) registerDoParallel(cluster,cores=nproc) } else if (nproc == 1) { # If needed to run on a single core/cpu then, regsiter DoSEQ # (needed for 'foreach' in non-parallel mode) registerDoSEQ() } # Printing status to console # cat("Calculating the expected frequencies of mutations...\n") # Calculate targeting for each sequence (based on the germline) # Should be a 5 x N matrix where N in the number of nucleotides defined by # the regionDefinition numbOfSeqs <- nrow(db) targeting_list <- foreach (idx=iterators::icount(numbOfSeqs)) %dopar% { rd <- regionDefinition if (regionDefinitionName %in% c("IMGT_VDJ_BY_REGIONS","IMGT_VDJ")) { rd <- setRegionBoundaries(juncLength = db[[juncLengthColumn]][idx], sequenceImgt = db[[sequenceColumn]][idx], regionDefinition=regionDefinition) } eM <- calcExpectedMutations(germlineSeq=db[[germlineColumn]][idx], inputSeq=db[[sequenceColumn]][idx], targetingModel=targetingModel, regionDefinition=rd, mutationDefinition=mutationDefinition) eM['tmp_expmu_row_id'] <- db[['tmp_expmu_row_id']][idx] eM } # Convert list of expected mutation freq to data.frame if (is.null(regionDefinition)) { labels_length <- length(makeNullRegionDefinition()@labels) + 1 # +1 for tmp row_id } else { labels_length <- length(regionDefinition@labels) + 1 } expectedMutationFrequencies <- as.data.frame(do.call(rbind, lapply(targeting_list, function(x) { length(x) <- labels_length return(x) })), stringsAsFactors=F) expectedMutationFrequencies[is.na(expectedMutationFrequencies)] <- 0 col_names <- colnames(expectedMutationFrequencies) mu_col_names <- col_names != "tmp_expmu_row_id" colnames(expectedMutationFrequencies)[mu_col_names] <- paste0("mu_expected_", colnames(expectedMutationFrequencies)[mu_col_names]) # Properly shutting down the cluster if(nproc>1){ parallel::stopCluster(cluster) } # Bind the observed mutations to db db_new <- db %>% ungroup() %>% left_join(expectedMutationFrequencies, by="tmp_expmu_row_id") %>% arrange(!!rlang::sym("tmp_expmu_row_id")) %>% select(-!!rlang::sym("tmp_expmu_row_id")) return(db_new) } #' Calculate expected mutation frequencies of a sequence #' #' \code{calcExpectedMutations} calculates the expected mutation #' frequencies of a given sequence. This is primarily a helper function for #' \link{expectedMutations}. #' #' @param germlineSeq germline (reference) sequence. #' @param inputSeq input (observed) sequence. If this is not \code{NULL}, #' then \code{germlineSeq} will be processed to be the same #' same length as \code{inputSeq} and positions in #' \code{germlineSeq} corresponding to positions with Ns in #' \code{inputSeq} will also be assigned an N. #' @param targetingModel \link{TargetingModel} object. Default is \link{HH_S5F}. #' @param regionDefinition \link{RegionDefinition} object defining the regions #' and boundaries of the Ig sequences. #' @param mutationDefinition \link{MutationDefinition} object defining replacement #' and silent mutation criteria. If \code{NULL} then #' replacement and silent are determined by exact #' amino acid identity. #' #' @return A \code{numeric} vector of the expected frequencies of mutations in the #' regions in the \code{regionDefinition}. For example, when using the default #' \link{IMGT_V} definition, which defines positions for CDR and #' FWR, the following columns are calculated: #' \itemize{ #' \item \code{mu_expected_cdr_r}: number of replacement mutations in CDR1 and #' CDR2 of the V-segment. #' \item \code{mu_expected_cdr_s}: number of silent mutations in CDR1 and CDR2 #' of the V-segment. #' \item \code{mu_expected_fwr_r}: number of replacement mutations in FWR1, #' FWR2 and FWR3 of the V-segment. #' \item \code{mu_expected_fwr_s}: number of silent mutations in FWR1, FWR2 and #' FWR3 of the V-segment. #' } #' #' @details #' \code{calcExpectedMutations} calculates the expected mutation frequencies of a #' given sequence and its germline. #' #' Note, only the part of the sequences defined in \code{regionDefinition} are analyzed. #' For example, when using the default \link{IMGT_V} definition, mutations in #' positions beyond 312 will be ignored. #' #' @seealso \link{expectedMutations} calls this function. #' To create a custom \code{targetingModel} see \link{createTargetingModel}. #' See \link{calcObservedMutations} for getting observed mutation counts. #' #' @examples #' # Load example data #' data(ExampleDb, package="alakazam") #' #' # Use first entry in the exampled data for input and germline sequence #' in_seq <- ExampleDb[["sequence_alignment"]][1] #' germ_seq <- ExampleDb[["germline_alignment_d_mask"]][1] #' #' # Identify all mutations in the sequence #' calcExpectedMutations(germ_seq,in_seq) #' #' # Identify only mutations the V segment minus CDR3 #' calcExpectedMutations(germ_seq, in_seq, regionDefinition=IMGT_V) #' #' # Define mutations based on hydropathy #' calcExpectedMutations(germ_seq, in_seq, regionDefinition=IMGT_V, #' mutationDefinition=HYDROPATHY_MUTATIONS) #' #' @export calcExpectedMutations <- function(germlineSeq, inputSeq=NULL, targetingModel=HH_S5F, regionDefinition=NULL, mutationDefinition=NULL) { # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Check mutation definition if (!is.null(mutationDefinition) & !is(mutationDefinition, "MutationDefinition")) { stop(deparse(substitute(mutationDefinition)), " is not a valid MutationDefinition object") } # Check targeting model if (!is(targetingModel, "TargetingModel")) { stop(deparse(substitute(targetingModel)), " is not a valid TargetingModel object") } # Mask ambiguous nucleotide characters germlineSeq <- gsub("[MRWSYKVHDB]", "N", germlineSeq) # Assign codon table codonTable <- if (is.null(mutationDefinition)) { CODON_TABLE } else { mutationDefinition@codonTable } # Get targeting targeting <- calculateTargeting(germlineSeq=germlineSeq, inputSeq=inputSeq, targetingModel=targetingModel, regionDefinition=regionDefinition) # Determine the mutations paths (i.e. determine R and S mutation frequencies) mutationalPaths <- calculateMutationalPaths(germlineSeq=c2s(colnames(targeting)), regionDefinition=regionDefinition, codonTable=codonTable) typesOfMutations <- c("r", "s") mutationalPaths[!(mutationalPaths %in% typesOfMutations)] <- NA if (is.null(regionDefinition)) { rdLength <- max(stri_length(inputSeq), stri_length(germlineSeq), na.rm=TRUE) regionDefinition <- makeNullRegionDefinition(rdLength) } listExpectedMutationFrequencies <- list() for(region in regionDefinition@regions){ for(typeOfMutation in typesOfMutations){ region_mutation <- paste(region, typeOfMutation, sep="_") targeting_region <- targeting[1:4, regionDefinition@boundaries %in% region] mutationalPaths_region <- mutationalPaths[, regionDefinition@boundaries[1:ncol(mutationalPaths)] %in% region] targeting_typeOfMutation_region <- sum(targeting_region[mutationalPaths_region == typeOfMutation], na.rm=TRUE) listExpectedMutationFrequencies[[region_mutation]] <- targeting_typeOfMutation_region } } expectedMutationFrequencies <- unlist(listExpectedMutationFrequencies) expectedMutationFrequencies[!is.finite(expectedMutationFrequencies)] <- NA expectedMutationFrequencies <- expectedMutationFrequencies / sum(expectedMutationFrequencies, na.rm=TRUE) return(expectedMutationFrequencies) } calculateTargeting <- function(germlineSeq, inputSeq=NULL, targetingModel=HH_S5F, regionDefinition=NULL) { # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Check targeting model if (!is(targetingModel, "TargetingModel")) { stop(deparse(substitute(targetingModel)), " is not a valid TargetingModel object") } # If an inputSequence is passed then process the germlineSequence # to be the same legth, mask germlineSequence with Ns where inputSequence is also N # If not needed then you may skip this step by passing in inputSequence=NULL # (which is default). if(!is.null(inputSeq)){ # Trim the input and germline sequence to the shortest len_inputSeq <- stri_length(inputSeq) len_germlineSeq <- stri_length(germlineSeq) # If a regionDefinition is passed, # then only analyze till the end of the defined length if(!is.null(regionDefinition)){ length_regionDefinition <- regionDefinition@seqLength } else{ length_regionDefinition <- max(len_inputSeq, len_germlineSeq, na.rm=TRUE) } len_shortest <- min( c(len_inputSeq,len_germlineSeq,length_regionDefinition), na.rm=TRUE) c_inputSeq <- s2c(inputSeq)[1:len_shortest] c_germlineSeq <- s2c(germlineSeq)[1:len_shortest] # If the sequence and germline (which now should be the same length) is shorter # than the length_regionDefinition, pad it with Ns if(len_shortest < length_regionDefinition){ fillWithNs <- array("N", length_regionDefinition - len_shortest) c_inputSeq <- c(c_inputSeq, fillWithNs) c_germlineSeq <- c( c_germlineSeq, fillWithNs) } # Mask germline with Ns where input sequence has Ns c_germlineSeq[c_inputSeq == "N" | !c_inputSeq %in% c(NUCLEOTIDES[1:5], ".") ] <- "N" s_germlineSeq <- c2s(c_germlineSeq) } else { s_germlineSeq <- germlineSeq } # Removing IMGT gaps (they should come in threes) # After converting ... to ZZZ any other . is not an IMGT gap & will be treated like N gaplessSeq <- gsub("\\.\\.\\.", "ZZZ", s_germlineSeq) #If there is a single gap left convert it to an N gaplessSeq <- gsub("\\.", "N", gaplessSeq) # Re-assigning s_germlineSeq (now has all "." that are not IMGT gaps converted to Ns) gaplessSeq <- gsub("ZZZ", "...", gaplessSeq) # Vector of seq c_germlineSeq <- s2c(gaplessSeq) # Matrix to hold targeting values for each position in c_germlineSeq germlineSeqTargeting <- matrix(NA, ncol=stri_length(gaplessSeq), nrow=length(NUCLEOTIDES[1:5]), dimnames=list(NUCLEOTIDES[1:5], c_germlineSeq)) # Now remove the IMGT gaps so that the correct 5mers can be made to calculate # targeting. e.g. # GAGAAA......TAG yields: "GAGAA" "AGAAA" "GAAAT" "AAATA" "AATAG" # (because the IMGT gaps are NOT real gaps in sequence!!!) gaplessSeq <- gsub("\\.\\.\\.", "", gaplessSeq) gaplessSeqLen <- stri_length(gaplessSeq) #Slide through 5-mers and look up targeting gaplessSeq <- paste("NN", gaplessSeq, "NN", sep="") gaplessSeqLen <- stri_length(gaplessSeq) pos <- 3:(gaplessSeqLen - 2) subSeq <- substr(rep(gaplessSeq, gaplessSeqLen - 4), (pos - 2), (pos + 2)) germlineSeqTargeting_gapless <- targetingModel@targeting[, subSeq] # germlineSeqTargeting_gapless <- sapply(subSeq, function(x) { # targetingModel@targeting[, x] }) germlineSeqTargeting[, c_germlineSeq != "."] <- germlineSeqTargeting_gapless # Set self-mutating targeting values to be NA mutatingToSelf <- colnames(germlineSeqTargeting) mutatingToSelf[!(mutatingToSelf %in% NUCLEOTIDES[1:5])] <- "N" # # TODO: What's with this <<- business? # # TODO: I think this is assigning NA to all self-mutations, which are already NA # sapply(1:ncol(germlineSeqTargeting), function(pos) { germlineSeqTargeting[mutatingToSelf[pos], pos] <<- NA }) germlineSeqTargeting[!is.finite(germlineSeqTargeting)] <- NA return(germlineSeqTargeting) } calculateMutationalPaths <- function(germlineSeq, inputSeq=NULL, regionDefinition=NULL, codonTable=NULL) { # Check region definition if (!is.null(regionDefinition) & !is(regionDefinition, "RegionDefinition")) { stop(deparse(substitute(regionDefinition)), " is not a valid RegionDefinition object") } # Set codon table if required if (is.null(codonTable)) { codonTable <- CODON_TABLE } # If an inputSequence is passed then process the germlineSequence # to be the same length, mask germlineSequence with Ns where inputSequence is also N # If this function is being called after running calculateTargeting you may skip # this step by passing in inputSequence=NULL (which is default). This way you save # some processing time. if(!is.null(inputSeq)){ # Trim the input and germline sequence to the shortest len_inputSeq <- stri_length(inputSeq) len_germlineSeq <- stri_length(germlineSeq) # If a regionDefinition is passed, # then only analyze till the end of the defined length if(!is.null(regionDefinition)){ length_regionDefinition <- regionDefinition@seqLength } else{ length_regionDefinition <- max(len_inputSeq, len_germlineSeq, na.rm=TRUE) } len_shortest <- min( c(len_inputSeq,len_germlineSeq,length_regionDefinition), na.rm=TRUE) c_inputSeq <- s2c(inputSeq)[1:len_shortest] c_germlineSeq <- s2c(germlineSeq)[1:len_shortest] # If the sequence and germline (which now should be the same length) is shorter # than the length_regionDefinition, pad it with Ns if(len_shortest0) { stop("Input nucleotides must be one of A, C, G, or T.") } # sort by alphabetical order (important) nucs <- sort(unique(nucs)) # concatenate nucs <- c2s(nucs) # convert return(IUPAC_DNA_2[nucs]) } # Convert one or more characters including dash and dots to ambiguous characters # # @param chars a character vector of nucleotides. One or more of # \code{c("A", "C", "G", "T", "N", "-", ".")}. # # @return a single IUPAC character or "-" or "." # chars2Ambiguous <- function(chars) { # chars must all be unique stopifnot(length(unique(chars)) == length(chars)) # input characters must be one of the characters allowed legal <- c("A", "C", "G", "T", "N", "-", ".") if (sum(! chars %in% legal) > 0) { stop("Input characters must be one of A, C, G, T, N, - (dash), or . (dot)") } # if any of A, T, G, C, N appears if (any(chars %in% c("A", "C", "G", "T", "N"))) { # ignore - and . idx.dash.dot <- which(chars == "-" | chars == ".") if (length(idx.dash.dot)>0) { chars <- chars[-idx.dash.dot] } # if only N appears if (sum(chars=="N") == length(chars)) { return("N") } else { # otherwise, if there are any of A, T, G, C # remove N # e.g. AGN would be treated as AG (R) # e.g. ATGN would be treated as AGT (D) # e.g. ATGCN would be treated as ACGT (N) idx.N <- which(chars == "N") if (length(idx.N) > 0) { chars <- chars[-idx.N] } return(nucs2IUPAC(chars)) } } else { # otherwise, if only one or both of - and . appear(s) # if both - and . appear, return - if (sum(chars %in% c("-", ".")) == 2) { return("-") } else { # if only - or . appears, return that return(chars) } } } # Convert IUPAC incomplete nucleic acid to one or more characters # # @param code a single IUPAC character. # @param excludeN if \code{TRUE}, do not translate when \code{code} # is \code{N}. Default is \code{TRUE}. # @return a character vector of nucleotides. One or more of # \code{c("A", "C", "G", "T")}. # IUPAC2nucs <- function(code, excludeN=TRUE) { # input character must be one of IUPAC codes if (! code %in% names(IUPAC_DNA) ) { stop("Input character must be one of IUPAC DNA codes.") } # convert if (code == "N" & excludeN) { return(code) } else { return(IUPAC_DNA[[code]]) } } # Given a nuclotide position, returns the codon number # e.g. nuc 86 = codon 29 getCodonNumb <- function(nucPos){ return( ceiling(nucPos/3) ) } # Given a codon, returns all the nuc positions that make the codon getCodonNucs <- function(codonNumb){ getCodonPos(codonNumb*3) } # Given a nucleotide postions return the position in the codon getContextInCodon <- function(nucPos){ return((nucPos - 1)%%3 + 1 ) } # Given a nuclotide position, returns the pos of the 3 nucs that made the codon # e.g. nuc 86 is part of nucs 85,86,87 getCodonPos <- function(nucPos) { codonNum <- (ceiling(nucPos / 3)) * 3 return ((codonNum - 2):codonNum) } # Given two codons, tells you if the mutation is R or S (based on your definition) # # @param codonFrom starting codon. IUPAC ambiguous characters are allowed. # @param codonTo ending codon. IUPAC ambiguous characters are allowed. # @param ambiguousMode whether to consider ambiguous characters as "either or" # or "and" when determining (and counting) the type(s) of # mutations. Applicable only if \code{codonFrom} and/or # \code{codonTo} contains ambiguous characters. One of # \code{c("eitherOr", "and")}. Default is \code{"eitherOr"}. # @param aminoAcidClasses vector of amino acid trait classes. # if NULL then R or S is determined by amino acid identity # @return A vector with entries named by mutation type, including "r" (replacement), # "s" (silent), "stop" (stop) or "na" (input codons are identical or include NA). # Each entry indicates the count of its corresponding type of mutation. # # @details When there are ambiguous characters in \code{codonFrom} and/or \code{codonTo}: # \itemize{ # \item If \code{ambiguousMode="eitherOr"}, ambiguous characters will each # be expanded but only 1 mutation will be recorded. The priority for # different types of mutations, in decreasing order, is as follows: # no mutation ("na"), replacement mutation ("r"), silent mutation ("s"), # and stop mutation ("Stop"). The returned vector will have exactly one # entry with a count of 1 and 0 in all other entries. # \item If \code{ambiguousMode="and"}, ambiguous characters will each be # expanded and mutation(s) from each expansion will be recorded. # Each entry in the returned vector could potentially be greater than 1. # } # # @examples # # Without classes # mutationType("TTT", "TTC") # mutationType("TTT", "TTA") # mutationType("TTT", "TGA") # mutationType("TGG", "TWG") # # # With classes # classes <- HYDROPATHY_MUTATIONS@classes # mutationType("TTT", "TTC", aminoAcidClasses=classes) # mutationType("TTT", "TTA", aminoAcidClasses=classes) # mutationType("TTT", "TCT", aminoAcidClasses=classes) # mutationType("TTT", "TGA", aminoAcidClasses=classes) # mutationType <- function(codonFrom, codonTo, ambiguousMode=c("eitherOr", "and"), aminoAcidClasses=NULL) { # codonFrom="TTT"; codonTo="TTA" # codonFrom="TTT"; codonTo="TGA" ambiguousMode <- match.arg(ambiguousMode) # placeholder for tabulation tab <- setNames(object=rep(0, 4), nm=c("r", "s", "stop", "na")) if (grepl(pattern="[-.]", x=codonFrom) | grepl(pattern="[-.]", x=codonTo)) { # "na" tab[4] <- 1 } else { codonFrom.all <- EXPANDED_AMBIGUOUS_CODONS[[codonFrom]] codonTo.all <- EXPANDED_AMBIGUOUS_CODONS[[codonTo]] for (cur.codonFrom in codonFrom.all) { for (cur.codonTo in codonTo.all) { # if codons are the same, there is no mutation; count as NA if (cur.codonFrom == cur.codonTo) { # "na" tab[4] <- tab[4] + 1 } else { # Translate codons cur.aaFrom <- AMINO_ACIDS[cur.codonFrom] cur.aaTo <- AMINO_ACIDS[cur.codonTo] # If any codon is NA then return NA if (any(is.na(c(codonFrom, codonTo, cur.aaFrom, cur.aaTo)))) { # "na" tab[4] <- tab[4] + 1 } else if (any(c(cur.aaFrom, cur.aaTo) == "*")) { # If any amino acid is Stop then return "stop" tab[3] <- tab[3] + 1 } else if (is.null(aminoAcidClasses)) { # Check for exact identity if no amino acid classes are specified mutation <- if (cur.aaFrom == cur.aaTo) { "s" } else { "r" } tab[mutation] <- tab[mutation]+1 } else { # Check for amino acid class identity if classes are specified mutation <- if (aminoAcidClasses[cur.aaFrom] == aminoAcidClasses[cur.aaTo]) { "s" } else { "r" } tab[mutation] <- tab[mutation]+1 } } } } # if there's ambiguous char in observed or germline if ((length(codonFrom.all) > 1) | (length(codonTo.all) > 1)) { if (ambiguousMode=="eitherOr") { if (tab[4] > 0) { # "na" tab <- setNames(object=c(0, 0, 0, 1), nm=c("r", "s", "stop", "na")) } else if (tab[2] > 0) { # "S" tab <- setNames(object=c(0, 1, 0, 0), nm=c("r", "s", "stop", "na")) } else if (tab[1] > 0) { # "R" tab <- setNames(object=c(1, 0, 0, 0), nm=c("r", "s", "stop", "na")) } else { tab <- setNames(object=c(0, 0, 1, 0), nm=c("r", "s", "stop", "na")) } stopifnot(sum(tab) == 1) } else { stopifnot(sum(tab) >= 1) } } else { # no need to do anything if there isn't ambiguous char in observed or germline # there should be only 1 mutation stopifnot(sum(tab) == 1) } } return(tab) } # returns a boolean vector indicating whether ambiguous characters # exist in each entry of input character vector # input: # - seqs: a character vector # output: # - a boolean vector, where a TRUE indicates presence of ambiguous # character(s) checkAmbiguousExist <- function(seqs) { # ^ within brackets negates the character class bool <- stri_detect_regex(str=seqs, pattern="[^atgcnATGCN\\-\\.]") return(bool) } shazam/R/kedd.R0000644000176200001440000006075714241221623013004 0ustar liggesusers# This file contains functions adapted from the CRAN package kedd, # scheduled for archival on 2022-05-25 # Original code: https://github.com/cran/kedd # Modification: Simplify to use only kernel="gaussian" # # kedd: Kernel Estimator and Bandwidth Selection for Density and Its Derivatives # Smoothing techniques and computing bandwidth selectors of the nth derivative of a probability density for one-dimensional data. # # Version: 1.0.3 # Depends: R (≥ 2.15.0) # Published: 2015-10-31 # Author: Arsalane Chouaib Guidoum # Maintainer: Arsalane Chouaib Guidoum # License: GPL-2 | GPL-3 [expanded from: GPL (≥ 2)] # NeedsCompilation: no # Classification/MSC: 62G05, 62G07, 65D10, 68N15 h.ucv <- function(x,deriv.order=0,lower=0.1*hos,upper=2*hos,tol=0.1 * lower, kernel="gaussian",...) { if (!is.numeric(x) || length(dim(x)) >=1 || length(x) < 3L) stop("argument 'x' must be numeric and need at least 3 data points") if (any(deriv.order < 0 || deriv.order != round(deriv.order))) stop("argument 'deriv.order' is non-negative integers") r <- deriv.order if (missing(kernel)) kernel <- "gaussian" if (kernel != "gaussian") {stop("Expecting gaussian kernel.")} name <- deparse(substitute(x)) x <- x[!is.na(x)] x <- sort(x) n <- length(x) hos <- ((243 *(2*r+1)*A3_kMr(kernel,r))/(35* A2_kM(kernel)^2))^(1/(2*r+5)) * sd(x,na.rm = TRUE) * n^(-1/(2*r+5)) if (!is.numeric(upper)){ stop("argument 'upper' must be numeric. Default 2*hos (Oversmoothing) boundary was used") upper= 2*hos } if (!is.numeric(lower)){ stop("argument 'lower' must be numeric. Default 0.1*hos boundary was used") lower=0.1*hos } if (lower < 0 | lower >= upper){ stop("the boundaries must be positive and 'lower' must be smaller than 'upper'. Default boundaries were used") upper=2*hos lower=0.1*hos } R_Kr1 <- A3_kMr(kernel,r) fucv <- function(h) { D <- kernel_fun_der(kernel, outer(x,x,"-")/h,deriv.order=2*r) diag(D) <- 0 D <- ((-1)^r / ((n-1)*h^(2*r+1)))* colSums(D) D1 <- mean(D) D2 <- kernel_fun_conv(kernel,outer(x,x,"-")/h,deriv.order=r) diag(D2) <- 0 D3 <- ((-1)^r / ((n-1)*h^(2*r+1)))* colSums(D2) D4 <- mean(D3) (1/(n*h^(2*r+1)))* R_Kr1 + D4 - 2*D1 } obj <- optimize(fucv , c(lower, upper),tol=tol) structure(list(x=x, data.name=name,n=n, kernel=kernel, deriv.order=r, h = obj$minimum , min.ucv=obj$objective),class="h.ucv") } A3_kMr <- function(kernel,r) { xKr <- integrate(function(x) kernel_fun_der(kernel,x,deriv.order=r)^2, -Inf, Inf)$value return(xKr) } A2_kM <-function(kernel) { xKr <- 1 return(xKr) } #### #### r(th) derivative of Kernel functions K^r(x) kernel_fun_der <- function(kernel,u,deriv.order=0) { if (any(deriv.order < 0 || deriv.order != round(deriv.order))) stop("argument 'deriv.order' is non-negative integers") r <- deriv.order Kr <- expression( dnorm(X) ) if (r == 0) { DKr <- Kr K <- function(X) eval(DKr);fx <- K(u) } else { if (r == 1){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)/sqrt(pi)} else if (r == 2){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^2-1)/sqrt(pi) } else if (r == 3){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^2-3)/sqrt(pi)} else if (r == 4){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^4-6*u^2+3)/sqrt(pi)} else if (r == 5){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^4-10*u^2+15)/sqrt(pi)} else if (r == 6){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^6-15*u^4+45*u^2-15)/sqrt(pi)} else if (r == 7){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^6-21*u^4+105*u^2-105)/sqrt(pi)} else if (r == 8){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^8-28*u^6+210*u^4-420*u^2+105)/sqrt(pi)} else if (r == 9){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^8-36*u^6+378*u^4-1260*u^2+945)/sqrt(pi)} else if (r == 10){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^10-45*u^8+630*u^6-3150*u^4+4725*u^2-945)/sqrt(pi)} else if (r == 11){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^10-55*u^8+990*u^6-6930*u^4+17325*u^2-10395)/sqrt(pi)} else if (r == 12){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^12-66*u^10+1485*u^8-13860*u^6+51975*u^4-62370*u^2+10395)/sqrt(pi)} else if (r == 13){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^12-78*u^10+2145*u^8-25740*u^6+135135*u^4-270270*u^2+135135)/sqrt(pi)} else if (r == 14){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^14-91*u^12+3003*u^10-45045*u^8+315315*u^6-945945*u^4+945945*u^2-135135)/sqrt(pi)} else if (r == 15){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^14-105*u^12+4095*u^10-75075*u^8+675675*u^6-2837835*u^4+4729725*u^2-2027025)/sqrt(pi)} else if (r == 16){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^16-120*u^14+5460*u^12-120120*u^10+1351350*u^8-7567560*u^6+18918900*u^4-16216200*u^2+2027025)/sqrt(pi)} else if (r == 17){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^16-136*u^14+7140*u^12-185640*u^10+2552550*u^8-18378360*u^6+64324260*u^4-91891800*u^2+34459425)/sqrt(pi)} else if (r == 18){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^18-153*u^16+9180*u^14-278460*u^12+4594590*u^10-41351310*u^8+192972780*u^6-413513100*u^4+310134825*u^2-34459425)/sqrt(pi)} else if (r == 19){fx <- -(1/2)*u*exp(-(1/2)*u^2)*sqrt(2)*(u^18-171*u^16+11628*u^14-406980*u^12+7936110*u^10-87297210*u^8+523783260*u^6-1571349780*u^4+1964187225*u^2-654729075)/sqrt(pi)} else if (r == 20){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^20-190*u^18+14535*u^16-581400*u^14+13226850*u^12-174594420*u^10+1309458150*u^8-5237832600*u^6+9820936125*u^4-6547290750*u^2+654729075)/sqrt(pi)} else if (r == 21){fx <- -(1/2)*exp(-(1/2)*u^2)*sqrt(2)*u*(u^20-210*u^18+17955*u^16-813960*u^14+21366450*u^12-333316620*u^10+3055402350*u^8-15713497800*u^6+41247931725*u^4-45831035250*u^2+13749310575)/sqrt(pi)} else if (r == 22){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^22-231*u^20+21945*u^18-1119195*u^16+33575850*u^14-611080470*u^12+6721885170*u^10-43212118950*u^8+151242416325*u^6-252070693875*u^4+151242416325*u^2-13749310575)/sqrt(pi)} else if (r == 23){fx <- -(1/2)*exp(-(1/2)*u^2)*sqrt(2)*u*(u^22-253*u^20+26565*u^18-1514205*u^16+51482970*u^14-1081142370*u^12+14054850810*u^10-110430970650*u^8+496939367925*u^6-1159525191825*u^4+1159525191825*u^2-316234143225)/sqrt(pi)} else if (r == 24){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^24-276*u^22+31878*u^20-2018940*u^18+77224455*u^16-1853386920*u^14+28109701620*u^12-265034329560*u^10+1490818103775*u^8-4638100767300*u^6+6957151150950*u^4-3794809718700*u^2+316234143225)/sqrt(pi)} else if (r == 25){fx <- -(1/2)*exp(-(1/2)*u^2)*sqrt(2)*u*(u^24-300*u^22+37950*u^20-2656500*u^18+113565375*u^16-3088978200*u^14+54057118500*u^12-602350749000*u^10+4141161399375*u^8-16564645597500*u^6+34785755754750*u^4-31623414322500*u^2+7905853580625)/sqrt(pi)} else if (r == 26){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^26-325*u^24+44850*u^22-3453450*u^20+164038875*u^18-5019589575*u^16+100391791500*u^14-1305093289500*u^12+10767019638375*u^10-53835098191875*u^8+150738274937250*u^6-205552193096250*u^4+102776096548125*u^2-7905853580625)/sqrt(pi)} else if (r == 27){fx <- -(1/2)*exp(-(1/2)*u^2)*sqrt(2)*u*(u^26-351*u^24+52650*u^22-4440150*u^20+233107875*u^18-7972289325*u^16+180705224700*u^14-2710578370500*u^12+26428139112375*u^10-161505294575625*u^8+581419060472250*u^6-1109981842719750*u^4+924984868933125*u^2-213458046676875)/sqrt(pi)} else if (r == 28){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^28-378*u^26+61425*u^24-5651100*u^22+326351025*u^20-12401338950*u^18+316234143225*u^16-5421156741000*u^14+61665657928875*u^12-452214824811750*u^10+2034966711652875*u^8-5179915266025500*u^6+6474894082531875*u^4-2988412653476250*u^2+213458046676875)/sqrt(pi)} else if (r == 29){fx <- -(1/2)*exp(-(1/2)*u^2)*sqrt(2)*u*(u^28-406*u^26+71253*u^24-7125300*u^22+450675225*u^20-18928359450*u^18+539458244325*u^16-10480903032600*u^14+137561852302875*u^12-1192202719958250*u^10+6557114959770375*u^8-21459648959248500*u^6+37554385678684875*u^4-28887988983603750*u^2+6190283353629375)/sqrt(pi)} else if (r == 30){fx <- (1/2)*exp(-(1/2)*u^2)*sqrt(2)*(u^30-435*u^28+82215*u^26-8906625*u^24+614557125*u^22-28392539175*u^20+899097073875*u^18-19651693186125*u^16+294775397791875*u^14-2980506799895625*u^12+19671344879311125*u^10-80473683597181875*u^8+187771928393424375*u^6-216659917377028125*u^4+92854250304440625*u^2-6190283353629375)/sqrt(pi)} else {K <- function(X) (-1)^r * Hermite(X,r) * eval(Kr);fx <- K(u)} } return(fx) } #### Hermite Polynomial Hermite <-function (x, n, prob = TRUE) { if (any(n < 0 || n != round(n))) stop("Argument 'n' must be a vector of non-negative integers") if ((length(n) != 1) && (length(x) != length(n)) && (length(x) != 1)) stop(paste("Argument 'n' must be either a vector of same length", "as argument 'x',\n a single integer or 'x' must be a ", "single value!", sep = "")) H <- function(x, n) { if (n <= 1) { return(switch(n + 1, 1, x)) } else { return(x * Recall(x, n - 1) - (n - 1) * Recall(x, n - 2)) } } scale <- 1 if (!prob) { x <- sqrt(2) * x scale <- 2^(n/2) } scale * mapply(H, x, n) } #### Kernels density convolution kernel_fun_conv <- function(kernel,u,deriv.order=0) { if (any(deriv.order < 0 || deriv.order != round(deriv.order))) stop("argument 'deriv.order' is non-negative integers") r <- deriv.order if (r==0) {fx <- dnorm(u,mean=0,sd=sqrt(2))} else if (r==1) {fx <- (1/8)*exp(-(1/4)*u^2)*(u^2-2)/sqrt(pi)} else if (r==2) {fx <- (1/32)*exp(-(1/4)*u^2)*(12-12*u^2+u^4)/sqrt(pi)} else if (r==3) {fx <- (1/128)*exp(-(1/4)*u^2)*(u^6-30*u^4+180*u^2-120)/sqrt(pi)} else if (r==4) {fx <- (1/512)*exp(-(1/4)*u^2)*(u^8-56*u^6+840*u^4-3360*u^2+1680)/sqrt(pi)} else if (r==5) {fx <- (1/2048)*exp(-(1/4)*u^2)*(u^10-90*u^8+2520*u^6-25200*u^4+75600*u^2-30240)/sqrt(pi)} else if (r==6) {fx <- (1/8192)*exp(-(1/4)*u^2)*(u^12-132*u^10+5940*u^8-110880*u^6+831600*u^4-1995840*u^2+665280)/sqrt(pi)} else if (r==7) {fx <- (1/32768)*exp(-(1/4)*u^2)*(u^14-182*u^12+12012*u^10-360360*u^8+5045040*u^6-30270240*u^4+60540480*u^2-17297280)/sqrt(pi)} else if (r==8) {fx <- (1/131072)*exp(-(1/4)*u^2)*(u^16-240*u^14+21840*u^12-960960*u^10+21621600*u^8-242161920*u^6+1210809600*u^4-2075673600*u^2+518918400)/sqrt(pi)} else if (r==9) {fx <- (1/524288)*exp(-(1/4)*u^2)*(u^18-306*u^16+36720*u^14-2227680*u^12+73513440*u^10-1323241920*u^8+12350257920*u^6-52929676800*u^4+79394515200*u^2-17643225600)/sqrt(pi)} else if (r==10) {fx <- (1/2097152)*exp(-(1/4)*u^2)*(u^20-380*u^18+58140*u^16-4651200*u^14+211629600*u^12-5587021440*u^10+83805321600*u^8-670442572800*u^6+2514159648000*u^4-3352212864000*u^2+670442572800)/sqrt(pi)} else if (r==11) {fx <- (1/8388608)*exp(-(1/4)*u^2)*(u^22-462*u^20+87780*u^18-8953560*u^16+537213600*u^14-19554575040*u^12+430200650880*u^10-5531151225600*u^8+38718058579200*u^6-129060195264000*u^4+154872234316800*u^2-28158588057600)/sqrt(pi)} else if (r==12) {fx <- (1/33554432)*exp(-(1/4)*u^2)*(u^24-552*u^22+127512*u^20-16151520*u^18+1235591280*u^16-59308381440*u^14+1799020903680*u^12-33924394183680*u^10+381649434566400*u^8-2374707592857600*u^6+7124122778572800*u^4-7771770303897600*u^2+1295295050649600)/sqrt(pi)} else if (r==13) {fx <- (1/134217728)*exp(-(1/4)*u^2)*(u^26-650*u^24+179400*u^22-27627600*u^20+2624622000*u^18-160626866400*u^16+6425074656000*u^14-167051941056000*u^12+2756357027424000*u^10-27563570274240000*u^8+154355993535744000*u^6-420970891461120000*u^4+420970891461120000*u^2-64764752532480000)/sqrt(pi)} else if (r==14) {fx <- (1/536870912)*exp(-(1/4)*u^2)*(u^28-756*u^26+245700*u^24-45208800*u^22+5221616400*u^20-396842846400*u^18+20238985166400*u^16-693908062848000*u^14+15786408429792000*u^12-231533990303616000*u^10+2083805912732544000*u^8-10608466464820224000*u^6+26521166162050560000*u^4-24481076457277440000*u^2+3497296636753920000)/sqrt(pi)} else if (r==15) {fx <- (1/2147483648)*exp(-(1/4)*u^2)*(u^30-870*u^28+328860*u^26-71253000*u^24+9832914000*u^22-908561253600*u^20+57542212728000*u^18-2515416727824000*u^16+75462501834720000*u^14-1526019481546560000*u^12+20143457156414592000*u^10-164810104007028480000*u^8+769113818699466240000*u^6-1774878043152614400000*u^4+1521324036987955200000*u^2-202843204931727360000)/sqrt(pi)} else if (r==16) {fx <- (1/8589934592)*exp(-(1/4)*u^2)*(u^32-992*u^30+431520*u^28-108743040*u^26+17670744000*u^24-1950850137600*u^22+150215460595200*u^20-8154553575168000*u^18+311911674250176000*u^16-8317644646671360000*u^14+151381132569418752000*u^12-1816573590833025024000*u^10+13624301931247687680000*u^8-58689300626913116160000*u^6+125762787057670963200000*u^4-100610229646136770560000*u^2+12576278705767096320000)/sqrt(pi)} else if (r==17) {fx <- (1/34359738368)*exp(-(1/4)*u^2)*(u^34-1122*u^32+556512*u^30-161388480*u^28+30502422720*u^26-3965314953600*u^24+364808975731200*u^22-24077392398259200*u^20+1143676138917312000*u^18-38884988723188608000*u^16+933239729356526592000*u^14-15440875522080712704000*u^12+169849630742887839744000*u^10-1175882058989223505920000*u^8+4703528235956894023680000*u^6-9407056471913788047360000*u^4+7055292353935341035520000*u^2-830034394580628357120000)/sqrt(pi)} else if (r==18) {fx <- (1/137438953472)*exp(-(1/4)*u^2)*(u^36-1260*u^34+706860*u^32-233735040*u^30+50837371200*u^28-7686610525440*u^26+832716140256000*u^24-65665615631616000*u^22+3792189302725824000*u^20-160114659448423680000*u^18+4899508579121764608000*u^16-106898368999020318720000*u^14+1621291929818474833920000*u^12-16462348825849129082880000*u^10+105829385309030115532800000*u^8-395096371820379097989120000*u^6+740805697163210808729600000*u^4-522921668585795864985600000*u^2+58102407620643984998400000)/sqrt(pi)} else if (r==19) {fx <- (1/549755813888)*exp(-(1/4)*u^2)*(u^38-1406*u^36+885780*u^34-331281720*u^32+82157866560*u^30-14295468781440*u^28+1801229066461440*u^26-167256984742848000*u^24+11540731947256512000*u^22-592424239959167616000*u^20+22512121118448369408000*u^18-626246278385927367168000*u^16+12524925567718547343360000*u^14-175348957948059662807040000*u^12+1653290174938848249323520000*u^10-9919741049633089495941120000*u^8+34719093673715813235793920000*u^6-61268988835969082180812800000*u^4+40845992557312721453875200000*u^2-4299578163927654889881600000)/sqrt(pi)} else if (r==20) {fx <- (1/2199023255552)*exp(-(1/4)*u^2)*(u^40-1560*u^38+1096680*u^36-460605600*u^34+129199870800*u^32-25633254366720*u^30+3716821883174400*u^28-401416763382835200*u^26+32615112024855360000*u^24-2000393537524462080000*u^22+92418181433630148096000*u^20-3192628085889041479680000*u^18+81412016190170557731840000*u^16-1502991068126225681203200000*u^14+19538883885640933855641600000*u^12-171942178193640217929646080000*u^10+967174752339226225854259200000*u^8-3185987419470392273402265600000*u^6+5309979032450653789003776000000*u^4-3353670967863570814107648000000*u^2+335367096786357081410764800000)/sqrt(pi)} else if (r==21) {fx <- (1/8796093022208)*exp(-(1/4)*u^2)*(u^42-1722*u^40+1343160*u^38-629494320*u^36+198290710800*u^34-44496435503520*u^32+7356744003248640*u^30-914338183260902400*u^28+86404958318155276800*u^26-6240358100755658880000*u^24+344467767161712370176000*u^22-14467646220791919547392000*u^20+458142130325077452334080000*u^18-10783960913805669262632960000*u^16+184867901379525758787993600000*u^14-2243063870071579206627655680000*u^12+18505276928090528454678159360000*u^10-97969113148714562407119667200000*u^8+304792796462667527488816742400000*u^6-481251783888422411824447488000000*u^4+288751070333053447094668492800000*u^2-27500101936481280675682713600000)/sqrt(pi)} else if (r==22) {fx <- (1/35184372088832)*exp(-(1/4)*u^2)*(u^44-1892*u^42+1629012*u^40-847086240*u^38+297750813360*u^36-75033204966720*u^34+14031209328776640*u^32-1988422807735203840*u^30+216240980341203417600*u^28-18164242348661087078400*u^26+1180675752662970660096000*u^24-59248455951814527670272000*u^22+2281065554144859315305472000*u^20-66677300813465118447390720000*u^18+1457375289208594731778682880000*u^16-23318004627337515708458926080000*u^14+265242302635964241183720284160000*u^12-2059528467526310578603004559360000*u^10+10297642337631552893015022796800000*u^8-30350945837229840105728488243200000*u^6+45526418755844760158592732364800000*u^4-26015096431911291519195847065600000*u^2+2365008766537390138108713369600000)/sqrt(pi)} else if (r==23) {fx <- (1/140737488355328)*exp(-(1/4)*u^2)*(u^46-2070*u^44+1958220*u^42-1124018280*u^40+438367129200*u^38-123268836731040*u^36+25886455713518400*u^34-4149229044366806400*u^32+514504401501483993600*u^30-49735425478476786048000*u^28+3759998166172845025228800*u^26-222181709819304478763520000*u^24+10220358651688006023121920000*u^22-363215822852296829437102080000*u^20+9858715191705199656149913600000*u^18-201117789910786072985458237440000*u^16+3016766848661791094781873561600000*u^14-32297150968026234073547116953600000*u^12+236845773765525716539345524326400000*u^10-1121901033626174446765320904704000000*u^8+3141322894153288450942898533171200000*u^6-4487604134504697787061283618816000000*u^4+2447784073366198792942518337536000000*u^2-212850788988365112429784203264000000)/sqrt(pi)} else if (r==24) {fx <- (1/562949953421312)*exp(-(1/4)*u^2)*(u^48-2256*u^46+2334960*u^44-1472581440*u^42+633946309920*u^40-197791248695040*u^38+46349082610871040*u^36-8342834869956787200*u^34+1170082590511439404800*u^32-128969103309705321062400*u^30+11220311987944362932428800*u^28-771141442080539852446924800*u^26+41770161446029242007541760000*u^24-1773625316785241660627927040000*u^22+58529635453912974800721592320000*u^20-1482750764832462028284947005440000*u^18+28357608377420836290949611479040000*u^16-400342706504764747636935691468800000*u^14+4047909587992621337217905324851200000*u^12-28122319242896106132250710677913600000*u^10+126550436593032477595128198050611200000*u^8-337467830914753273587008528134963200000*u^6+460183405792845373073193447456768000000*u^4-240095689978875846820796581281792000000*u^2+20007974164906320568399715106816000000)/sqrt(pi)} else if (r==25) {fx <- (1/2251799813685248)*exp(-(1/4)*u^2)*(u^50-2450*u^48+2763600*u^46-1906884000*u^44+901956132000*u^42-310633691860800*u^40+80764759883808000*u^38-16222178913804864000*u^36+2554993178924266080000*u^34-318522482972558504640000*u^32+31597430310877803660288000*u^30-2499069488223971744040960000*u^28+157441377758110219874580480000*u^26-7872068887905510993729024000000*u^24+310384430437417290609887232000000*u^22-9559840457472452550784526745600000*u^20+227046210864970748081132510208000000*u^18-4086831795569473465460385183744000000*u^16+54491090607592979539471802449920000000*u^14-521967288977995909272835160309760000000*u^12+3444984107254773001200712058044416000000*u^10-14764217602520455719431623105904640000000*u^8+37581644806415705467644131542302720000000*u^6-49019536704020485392579302011699200000000*u^4+24509768352010242696289651005849600000000*u^2-1960781468160819415703172080467968000000)/sqrt(pi)} else if (r==26) {fx <- (1/9007199254740992)*exp(-(1/4)*u^2)*(u^52-2652*u^50+3248700*u^48-2443022400*u^46+1264264092000*u^44-478397532412800*u^42+137300091802473600*u^40-30598306173122688000*u^38+5377652309926312416000*u^36-752871323389683738240000*u^34+84472162484322515430528000*u^32-7617853198586175937007616000*u^30+552294356897497755433052160000*u^28-32118041062654484854414417920000*u^26+1491194763623243939669240832000000*u^24-54875967301335376979828062617600000*u^22+1584543555826059010292535308083200000*u^20-35419208894935436700656671592448000000*u^18+602126551213902423911163417071616000000*u^16-7605809067965083249404169478799360000000*u^14+69212862518482257569577942257074176000000*u^12-435052278687602761865918494187323392000000*u^10+1779759321903829480360575658039050240000000*u^8-4333327044635410908704010297834209280000000*u^6+5416658805794263635880012872292761600000000*u^4-2599996226781246545222406178700525568000000*u^2+199999709752403580401723552207732736000000)/sqrt(pi)} else if (r==27) {fx <- (1/36028797018963968)*exp(-(1/4)*u^2)*(u^54-2862*u^52+3795012*u^50-3099259800*u^48+1747982527200*u^46-723664766260800*u^44+228195622960905600*u^42-56136123248382777600*u^40+10946544033434641632000*u^38-1710093434556567348288000*u^36+215471772754127485884288000*u^34-21978120820921003560197376000*u^32+1816857987862802960976316416000*u^30-121589726880049121234568867840000*u^28+6565845251522652546666718863360000*u^26-284519960899314943688891150745600000*u^24+9815938651026365557266744700723200000*u^22-266762568045540052203366826572595200000*u^20+5631654214294734435404410783199232000000*u^18-90699273135483617749144721034682368000000*u^16+1088391277625803412989736652416188416000000*u^14-9432724406090296245911050987606966272000000*u^12+56596346436541777475466305925641797632000000*u^10-221463964316902607512694240578598338560000000*u^8+516749250072772750862953228016729456640000000*u^6-620099100087327301035543873620075347968000000*u^4+286199584655689523554866403209265545216000000*u^2-21199969233754779522582696534019670016000000)/sqrt(pi)} else if (r==28) {fx <- (1/144115188075855872)*exp(-(1/4)*u^2)*(u^56-3080*u^54+4407480*u^52-3896212320*u^50+2386430046000*u^48-1076757236755200*u^46+371481246680544000*u^44-100406074102798464000*u^42+21612407450627369376000*u^40-3746150624775410691840000*u^38+526708777843422743272704000*u^36-60332096371155696047600640000*u^34+5641051010703057580450659840000*u^32-430455584816725624600542658560000*u^30+26749739913610806671605150924800000*u^28-1348186891645984656248899606609920000*u^26+54770092473118126660111546518528000000*u^24-1778417120303600348022445510483968000000*u^22+45646039421125742265909434769088512000000*u^20-912920788422514845318188695381770240000000*u^18+13967688062864477133368287039341084672000000*u^16-159630720718451167238494709021040967680000000*u^14+1320581416852641474427547138264975278080000000*u^12-7578989001067333679323314010912032030720000000*u^10+28421208754002501297462427540920120115200000000*u^8-63663507608965602906315837691661069058048000000*u^6+73457893394960311045749043490378156605440000000*u^4-32647952619982360464777352662390291824640000000*u^2+2331996615713025747484096618742163701760000000)/sqrt(pi)} else if (r==29) {fx <- (1/576460752303423488)*exp(-(1/4)*u^2)*(u^58-3306*u^56+5091240*u^54-4857042960*u^52+3220219482480*u^50-1577907546415200*u^48+593293237452115200*u^46-175445285932268352000*u^44+41492810122981465248000*u^42-7938957670197120350784000*u^40+1238477396550750774722304000*u^38-158299929050032326296323584000*u^36+16621492550253394261113976320000*u^34-1434562664721869873920760110080000*u^32+101649011671721065352099573514240000*u^30-5895642676959821790421775263825920000*u^28+278569116486351579597428881215774720000*u^26-10651172100948736866960516046485504000000*u^24+326635944429094597253455825425555456000000*u^22-7942410859275879154268241649821401088000000*u^20+150905806326241703931096591346606620672000000*u^18-2198913177896664828710264616764839329792000000*u^16+23988143758872707222293795819252792688640000000*u^14-189819224526731857150324819091478620405760000000*u^12+1044005734897025214326786505003132412231680000000*u^10-3758420645629290771576431418011276684034048000000*u^8+8095059852124626277241544592639672857919488000000*u^6-8994510946805140308046160658488525397688320000000*u^4+3854790405773631560591211710780796599009280000000*u^2-265847614191284935213187014536606662000640000000)/sqrt(pi)} else if (r==30) {fx <- (1/2305843009213693952)*exp(-(1/4)*u^2)*(u^60-3540*u^58+5851620*u^56-6007663200*u^54+4298483019600*u^52-2279915393595840*u^50+930965452384968000*u^48-300036865797212544000*u^46+77634539025028745760000*u^44-16320505315039376330880000*u^42+2810391015249780604177536000*u^40-398564543980877976592450560000*u^38+46698479069759536257415457280000*u^36-4526160279069001206487959705600000*u^34+362739416651101382405677913548800000*u^32-23989166754526171423095499349360640000*u^30+1304410942277360571130817777121484800000*u^28-58007921903628505398523425853167206400000*u^26+2094730513186584917168901489142149120000000*u^24-60857433856789203909328085368761384960000000*u^22+1405806722091830610305478772018387992576000000*u^20-25438407352137887234099139684142258913280000000*u^18+353825120443372431528833488333978692157440000000*u^16-3692088213322147111605219008702386352947200000000*u^14+27998335617692948929672910815993096509849600000000*u^12-147831212061418770348672969108443549572005888000000*u^10+511723426366449589668483354606150748518481920000000*u^8-1061352291723006556349446957701645996927221760000000*u^6+1137163169703221310374407454680334996707737600000000*u^4-470550277118574335327341015729793791741132800000000*u^2+31370018474571622355156067715319586116075520000000)/sqrt(pi)} else if (r>=31) {fx <- NA} return(fx) } shazam/NEWS.md0000644000176200001440000007113014506546056012650 0ustar liggesusersVersion 1.2.0: October 10, 2023 ------------------------------------------------------------------------------- General: + Updated dependencies: alakazam >= 1.3.0, ggplot2 >= 3.4.0, igraph >= 1.5.0. + As of `alakazam 1.3.0`, `alakazam::makeChangeoClone` requires the parameter `locus` with default value `locus`. This function is used in some examples and tests in `shazam`. We added a `locus` column to the package's example data. Distance Profiling: + Added to `distToNearest` the parameter `locusValues=c("IGH")` to specify loci values to focus the analysis on. + Fixed a bug in `distToNearest` where grouping by `fields` was applied after grouping by genes, therefore not treating independently the different subsets of data to identify groups of genes. In practice, this means that if fields was set to treat samples independently (`fields='sample_id'`), single linkage was applied to all data, and two genes could be placed in the same group of genes if they where connected by an ambiguous gene call in any of the samples. Now, data is separated by `fields`(sample_id in this example) before creating the groups of genes, and ambiguities in other samples are not considered. Version 1.1.2: September 26, 2022 ------------------------------------------------------------------------------- Mutation Profiling: + Bug fix in parallelization set up for functions `slideWindowTune` and `slideWindowDb`. + `plotSlideWindowTune` (`slideWindowTunePlot`). Updated the possible values of the parameter `plotFiltered`, for easier usage. The new values (and their equivalent values in `slideWindowTunePlot`) are `filtered` (`TRUE`), `remaining` (`FALSE`), and `per_mutation` (`NULL`). Deprecated: + Deprecated `slideWindowTunePlot` in favor of `plotSlideWindowTune`, for naming consistency. Version 1.1.1: May 23, 2022 ------------------------------------------------------------------------------- General: + Removed dependency: kedd. The CRAN kedd package (by Arsalane Chouaib Guidoum) has been scheduled for archival on 2022-05-25. We have adapted the functions used by shazam and removed the dependency. New feature: + Added the function `convertNumbering` to convert between numbering systems (IMGT, Kabat). Mutation Profiling: + `shmulateTree` has new argument `nproc` to specify the number of cores. Default values `mutThresh` and `windowSize` have been set to `mutThresh=6` and `windowSize=10`. + Added the option `plotFiltered=NULL` to `slideWindowTunePlot`. + Fixed a bug in `listObservedMutations` not returning a list when `db` had one sequence with one mutation. + Fixed bars shifted in `plotMutability`. Version 1.1.0: July 8, 2021 ------------------------------------------------------------------------------- General: + Updated dependencies to alakazam >= 1.1.0 and ggplot2 >= 3.3.4. Selection Analysis: + `observedMutations`, `expectedMutations`, and `calcBaseline` can analyze mutations in all regions (CDR1, CDR2, CDR3, FWR1, FWR2, FWR3 and FWR4) by specifying `regionDefinition=IMGT_VDJ` or `regionDefinition=IMGT_VDJ_BY_REGIONS`. + Added the function `setRegionBoundaries` to build sequence-specific `RegionDefinition` objects extending to CDR3 and FWR4. + Added the function `makeGraphDf` to facilitate mutational analysis on lineage trees. Distance Profiling: + Fixed a bug in `distToNearest` where TRB and TRD sequences where ignored in distance calculation. + Fixed a bug in `distToNearest` causing a fatal error when `cross` was set. + Fixed a bug in `nearestDist` causing a fatal error when using `model="aa"` and `crossGroups`. Targeting Models: + Fixed an incompatibility with newer versions of ggplot2 in `plotMutability`. Version 1.0.2: August 10, 2020 ------------------------------------------------------------------------------- Mutation Profiling: + Fixed a bug in `observedMutations` and `calcObservedMutations` causing mutation counting to fail when there are gap (`-`) characters in the germline sequence. Targeting Models: + Fixed a bug in `createTargetingModel` causing empty counts in the `numMutS` and `numMutR` slots. Version 1.0.1: July 18, 2020 ------------------------------------------------------------------------------- Distance Profiling: + Added support for TCR genes to `distToNearest`. + Renamed the `groupUsingOnlyIGH` argument of `distToNearest` to `onlyHeavy`. Version 1.0.0 May 9, 2020 ------------------------------------------------------------------------------- Backwards Incompatible Changes: + Changed default expected data format from the Change-O data format to the AIRR Rearrangement standard. For example: where functions used the column name `V_CALL` (Change-O) as the default to identify the field that stored the V gene calls, they now use `v_call` (AIRR). That means, scripts that relied on default values (previously, `v_call="V_CALL"`), will now fail if calls to the functions are not updated to reflect the correct value for the data. If data are in the Change-O format, the current default value `v_call="v_call"` will fail to identify the column with the V gene calls as the column `v_call` doesn't exist. In this case, `v_call="V_CALL"` needs to be specified in the function call. + `ExampleDb` converted to the AIRR Rearrangement standard and examples updated accordingly. + For consistency with the style of the new data format default, other field names have been updated to use the same capitalization. This change affects: - Region definitions. For example, the `labels` slot of `IMGT_V` has changed from `CDR_R`, `CDR_S`, `FWR_R` and `FWR_S` to `cdr_r`, `cdr_s`, `fwr_r` and `fwr_s`, respectively. - Mutations in `CODON_TABLE` and the different `MUTATION_SCHEMES` change from `R`, `S` and `Stop` to `r`, `s` and `stop`, respectively. - Mutation profiling function output columns. For example, from `MU_COUNT_SEQ` to `mu_count_seq`. - `calcBaseline` and related function output columns and S4 object slots. For example, from `PVALUE`, `REGION` and `BASELINE_CI_PVALUE` to `pvalue`, `region` and `baseline_ci_pvalue`, respectively. + Model names used by `createSubstitutionMatrix`, `createMutabilityMatrix` and `createTargetingModel`, changed from `model=c("S","RS")` to `model=c("s","rs")`. General: + License changed to AGPL-3. Targeting Models: + `createMutabilityMatrix`, `extendMutabilityMatrix`, `createTargetingMatrix`, and `createTargetingModel` now also returns the numbers of silent and replacement mutations used for estimating the 5-mer mutabilities. These numbers are recorded in the `numMutS` and `numMutR` slots in the newly defined `MutabilityModel`, `MutabilityModelWithSource`, and `TargetingMatrix` classes. Mutation Profiling: + `shmulateSeq` now also supports specifying the frequency of mutations to be introduced. (Previously, only the number of mutations was supported.) Version 0.2.3 February 5, 2020 ------------------------------------------------------------------------------- General: + Removed SDMTools dependency. Version 0.2.2 December 15, 2019 ------------------------------------------------------------------------------- General: + Fixed an incompatibility with R 4.0 matrix changes. Version 0.2.1 July 19, 2019 ------------------------------------------------------------------------------- Distance Calculation: + Fixed a bug in `distToNearest` that could potentially cause sequences from different partitions to be used for distance calculation. Version 0.2.0 July 18, 2019 ------------------------------------------------------------------------------- General: + Upgraded to alakazam >= 0.3.0 and dplyr >= 0.8.1. Distance Calculation: + Fixed a bug in `plotDensityThreshold` for negative densities. + Fixed a bug in `distToNearest` for performing subsampling while calculating cross-group nearest neighbor distances. + For partitioning sequences, `distToNearest` now supports, via a new argument `VJthenLen`, either a 2-stage partitioning (first by V gene and J gene, then by junction length), or a 1-stage partitioning (simultaneously by V gene, J gene, and junction length). For 1-stage partitioning, `distToNearest` supports export of the partitioning information as a new column via `keepVJLgroup`. + `distToNearest` now supports single-cell input data with the addition of new arguments `cellIdColumn`, `locusColumn`, and `groupUsingOnlyIGH`. Mutation Profiling: + `shmulateTree` has new arguments, `start` and `end`, to specify the region in the sequence where mutations can be introduced. Selection Analysis: + Added the function `consensusSequence` which can be used to build a consensus sequence using a variety of methods. Version 0.1.11: January 27, 2019 ------------------------------------------------------------------------------- General: + Fixed a bug in the prototype declarations for the `TargetingModel` and `RegionDefinition` S4 classes. Version 0.1.10: September 19, 2018 ------------------------------------------------------------------------------- General: + Added `subsample` argument to `distToNearest` function. + Removed some internal utility functions in favor of importing them from `alakazam`. Specifically, `progressBar`, `getBaseTheme` and `checkColumns`. + Removed `clearConsole`, `getnproc`, and `getPlatform` functions. Distance Calculation: + Changed default `findThreshold` method to `density`. + Significantly reduced run time of the `density` method by retuning the bandwidth detection process. The `density` method should now also yield more consistent thresholds, on average. + The `subsample` argument to `findThreshold` now applies to both the `density` and `gmm` methods. Subsampling of distance is not performed by default. + Fixed a bug in `plotDensityThreshold` and `plotGmmThreshold` wherein the `breaks` argument was ignored when specifying `xmax` and/or `xmin`. Selection Analysis: + Fixed a plotting bug in `plotBaselineDensity` arising when the `groupColumn` and `idColumn` arguments were set to the same column. + Added the `sizeElement` argument to `plotBaselineDensity` to control line size + Renamed the `field_name` argument to `field` in `editBaseline`. Version 0.1.9: March 30, 2018 ------------------------------------------------------------------------------- Selection Analysis: + Fixed a bug in `plotBaselineDensity` which caused an empty plot to be generated if there was only a single value in the `idColumn`. + Fixed a bug in `calcBaseline` which caused a crash in `summarizeBaseline` and `groupBaseline` when input `baseline` is based on only 1 sequence (i.e. when `nrow(baseline@db)` is 1). + Set default `plot` call on a `Baseline` object to `plotBaselineDensity`. + Removed `getBaselineStats` function. + Added a `summary` method for `Baseline` objects that calls `summarizeBaseline` and returns a data.frame. Mutation Profiling: + Fixed a bug in `shmulateSeq` which caused a crash when the input sequence contains gaps (`.`). + Renamed the argument `mutations` in `shmulateSeq` to `numMutations`. + Improved help documentation for `shmulateSeq` and `shmulateTree`. + Added vignette for simulating mutated sequences. + `calcExpectedMutations` will now treat non-ACTG characters as Ns rather than produce an error. + Added two new `RegionDefinition` objects for the full V segment as single region (`IMGT_V_BY_SEGMENTS`) and the V segment with each codon as a separate region (`IMGT_V_BY_CODONS`). Targeting Models: + Added the `calculateMutability` function which computes the aggregate mutability for sequences. + Fixed a bug that caused `createSubstitutionMatrix` to fail for data containing only a single V family. + Changed the default model to silent mutations only (`model="S"`) in `createSubstitutionMatrix`, `createSubstitutionMatrix` and `createTargetingModel` + Set default `plot` call on a `TargetingModel` object to `plotMutability`. Version 0.1.8: June 30, 2017 ------------------------------------------------------------------------------- General: + Corrected several functions so that they accept both tibbles and data.frames. Distance Calculation: + Adding new fitting procedures to the `"gmm"` method of `findThreshold()` that allows users to choose a mixture of two univariate density distribution functions among four available combinations: `"norm-norm"`, `"norm-gamma"`, `"gamma-norm"`, or `"gamma-gamma"`. + Added the ability to choose the threshold selection criteria in the `"gmm"` method of `findThreshold()` from the best average sensitivity and specificity, the curve intersection or user defined sensitivity or specificity. + Renamed the `cutEdge` argument of `findThreshold()` to `edge`. Mutation Profiling: + Redesigned `collapseClones()`, adding various deterministic and stochastic methods to obtain effective clonal sequences, support for including ambiguous IUPAC characters in output, as well as extensive documentation. Removed `calcClonalConsensus()` from exported functions. + Added support for including ambiguous IUPAC characters in input for `observedMutations()` and `calcObservedMutations()`. + Fixed a minor bug in calculating the denominator for mutation frequency in `calcObservedMutations()` for sequences with non-triplet overhang at the tail. + Renamed column names of observed mutations (previously `OBSERVED`) and expected mutations (previously `EXPECTED`) returned by `observedMutations()` and `expectedMutations()` to `MU_COUNT` and `MU_EXPECTED` respectively. Selection Analysis: + `calcBaseline()` no longer calls `collapseClones()` automatically if a `CLONE` column is present. As indicated by the documentation for `calcBaseline()` users are advised to obtain effective clonal sequences (for example, calling `collapseClones()`) before running `calcBaseline()`. + Updated vignette to reflect changes in `calcBaseline()`. Version 0.1.7: May 14, 2017 ------------------------------------------------------------------------------- Mutation Profiling: + Fixed a bug in `collapseClones()` that prevented it from running when `nproc` is greater than 1. Version 0.1.6: May 12, 2017 ------------------------------------------------------------------------------- General: + Internal changes for compatibility with dplyr v0.6.0. + Removed data.table dependency. Mutation Profiling: + Fixed a bug in `collapseClones()` that resulted in erroneous `CLONAL_SEQUENCE` and `CLONAL_GERMLINE` being returned. + Added a vignette describing basic mutational analysis. + Remove console notification that `observedMutations` was running. Version 0.1.5: March 23, 2017 ------------------------------------------------------------------------------- General: + License changed to Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0). Selection Analysis: + Fixed a bug in p-value calculation in `summarizeBaseline()`. The returned p-value can now be either positive or negative. Its magnitude (without the sign) should be interpreted as per normal. Its sign indicates the direction of the seLicense chalection detected. A positive p-value indicates positive selection, whereas a negative p-value indicates negative selection. + Added `editBaseline()` to exported functions, and a corresponding section in the vignette. + Fixed a bug in counting the total number of observed mutations when performing a local test for codon-by-codon selection analysis in `calcBaseline()`. Targeting Models: + Added `numMutationsOnly` argument to `createSubstitutionMatrix()`, enabling parameter tuning for `minNumMutations`. + Added functions `minNumMutationsTune()` and `minNumSeqMutationsTune()` to tune for parameters `minNumMutations` and `minNumSeqMutations` in functions `createSubstitutionMatrix()` and `createMutabilityMatrix()` respectively. Also added function `plotTune()` which helps visualize parameter tuning using the abovementioned two new functions. + Added human kappa and lambda light chain, silent, 5-mer, functional targeting model (`HKL_S5F`). + Renamed `HS5FModel` as `HH_S5F`, `MRS5NFModel` as `MK_RS5NF`, and `U5NModel` as `U5N`. + Added human heavy chain, silent, 1-mer, functional substitution model (`HH_S1F`), human kappa and lambda light chain, silent, 1-mer, functional substitution model (`HKL_S1F`), and mouse kappa light chain, replacement and silent, 1-mer, non-functional substitution model (`MK_RS1NF`). + Added `makeDegenerate5merSub` and `makeDegenerate5merMut` which make degenerate 5-mer substitution and mutability models respectively based on the 1-mer models. Also added `makeAverage1merSub` and `makeAverage1merMut` which make 1-mer substitution and mutability models respectively by averaging over the 5-mer models. Mutation Profiling: + Added `returnRaw` argument to `calcObservedMutations()`, which if true returns the positions of point mutations and their corresponding mutation types, as opposed to counts of mutations (hence "raw"). + Added new functions `slideWindowSeq()` and `slideWindowDb()` which implement a sliding window approach towards filtering a single sequence or sequences in a data.frame which contain(s) equal to or more than a given number of mutations in a given number of consecutive nucleotides. + Added new function `slideWindowTune()` which allows for parameter tuning for using `slideWindowSeq()` and `slideWindowDb()`. + Added new function `slideWindowTunePlot()` which visualizes parameter tuning by `slideWindowTune()`. Distance Calculation: + Fixed a bug in `distToNearest` wherein `normalize="length"` for 5-mer models was resulting in distances normalized by junction length squared instead of raw junction length. + Fixed a bug in `distToNearest` wherein `symmetry="min"` was calculating the minimum of the total distance between two sequences instead of the minimum distance at each mutated position. + Added `findThreshold` function to infer clonal distance threshold from nearest neighbor distances returned by `distToNearest`. + Renamed the `length` option for the `normalize` argument of `distToNearest` to `len` so it matches Change-O. + Deprecated the `HS1FDistance` and `M1NDistance` distance models, which have been renamed to `hs1f_compat` and `m1n_compat` in the `model` argument of `distToNearest`. These deprecated models should be used for compatibility with DefineClones in Change-O v0.3.3. These models have been replaced by replaced by `hh_s1f` and `mk_rs1nf`, which are supported by Change-O v0.3.4. + Renamed the `hs5f` model in `distToNearest` to `hh_s5f`. + Added support for `MK_RS5NF` models to `distToNearest`. + Updated `calcTargetingDistance()` to enable calculation of a symmetric distance matrix given a 1-mer substitution matrix normalized by row, such as `HH_S1F`. + Added a Gaussian mixture model (GMM) approach for threshold determination to `findThreshold`. The previous smoothed density method is available via the `method="density"` argument and the new GMM method is available via `method="gmm"`. + Added the functions `plotGmmThreshold` and `plotDensityThreshold` to plot the threshold detection results from `findThreshold` for the `"gmm"` and `"density"` methods, respectively. Region Definition: + Deleted `IMGT_V_NO_CDR3` and `IMGT_V_BY_REGIONS_NO_CDR3`. Updated `IMGT_V` and `IMGT_V_BY_REGIONS` so that neither includes CDR3 now. Version 0.1.4: August 5, 2016 ------------------------------------------------------------------------------- Selection Analysis: + Fixed a bug in calcBaseline wherein the germline column was incorrected hardcoded, leading to erroneous mutation counts for some clonal consensus sequences. Targeting Models: + Added `numSeqMutationsOnly` argument to `createMutabilityMatrix()`, enabling parameter tuning for `minNumSeqMutations`. Version 0.1.3: July 31, 2016 ------------------------------------------------------------------------------- General: + Added ape and igraph dependency + Removed the `InfluenzaDb` data object, in favor of the updated `ExampleDb` provided in alakazam 0.2.4. + Added conversion of sequence to uppercase for several functions to support data that was not generated via Change-O. Distance Calculation: + Added the `cross` argument to `distToNearest()` which allows restriction of distances to only distances across samples (ie, excludes within-sample distances). + Added `mst` flag to `distToNearest()`, which will return all distances to neighboring nodes in a minimum spanning tree. + Updated single nucleotide distance models to use the new C++ distance methods in alakazam 0.2.4 for better performance. + Fixed a bug leading to failed distance calculations for the `aa` model of `distToNearest()`. + Fixed a bug wherein gap characters where being translated into Ns (Asn) rather than Xs within the `aa` model of `distToNearest()`. Mutation Profiling: + Added the `MutationDefinition` `VOLUME_MUTATIONS`. + Added the functions `shmulateSeq()` and `shmulateTree()` to simulate mutations on sequences and lineage trees, respectively, using a 5-mer targeting model. + Renamed `collapseByClone`, `calcDbExpectedMutations` and `calcDbObservedMutations` to `collapseClones`, `expectedMutations`, and `observedMutations`, respectively. Selection Analysis: + Fixed a bug wherein passing a `Baseline` object through `groupBaseline()` multiple times resulted in incorrect normalization. + Added `title` options to `plotBaselineSummary()` and `plotBaselineDensity()`. + Added more control over colors and group ordering to `plotBaselineSummary()` and `plotBaselineDensity()`. + Added the `testBaseline()` function to test the significance of differences between two selection distributions. + Improved selection analysis vignette. Version 0.1.2: February 20, 2016 ------------------------------------------------------------------------------- General: + Renamed package from shm to shazam. + Internal changes to conform to CRAN policies. + Compressed and moved example database to the data object `InfluenzaDb`. + Fixed several bugs where functions would not work properly when passed a `dplyr::tbl_df` object instead of a `data.frame`. + Changed R dependency to R >= 3.1.2. + Added stringi dependency. Distance Calculation: + Fixed a bug wherein `distToNearest()` did not return the nearest neighbor with a non-zero distance. Targeting Models: + Performance improvements to `createSubstitutionMatrix()`, `createMutabilityMatrix()`, and `plotMutability()`. + Modified color scheme in `plotMutability()`. + Fixed errors in the targeting models vignette. Mutation Profiling: + Added the `MutationDefinition` objects `MUTATIONS_CHARGE`, `MUTATIONS_HYDROPATHY`, `MUTATIONS_POLARITY` providing alternate approaches to defining replacement and silent annotations to mutations when calling `calcDBObservedMutations()` and `calcDBExpectedMutations()`. + Fixed a few bugs where column names, region definitions or mutation models were not being recognized properly when non-default values were used. + Made the behavior of `regionDefinition=NULL` consistent for all mutation profiling functions. Now the entire sequence is used as the region and calculations are made accordingly. + `calcDBObservedMutations()` returns R and S mutations also when `regionDefinition=NULL`. Older versions reported the sum of R and S mutations. The function will add the columns `OBSERVED_SEQ_R` and `OBSERVED_SEQ_S` when `frequency=FALSE`, and `MU_FREQ_SEQ_R` and `MU_FREQ_SEQ_R` when `frequency=TRUE`. Version 0.1.1: December 18, 2015 ------------------------------------------------------------------------------- General: + Swapped dependency on doSNOW for doParallel. + Swapped dependency on plyr for dplyr. + Swapped dependency on reshape2 for tidyr. + Documentation clean up. Distance Calculation: + Changed underlying method of calcTargetingDistance to be negative log10 of the probability that is then centered at one by dividing by the mean distance. + Added `symmetry` parameter to distToNearest to change behavior of how asymmetric distances (A->B != B->A) are combined to get distance between A and B. + Updated error handling in distToNearest to issue warning when unrecognized character is in the sequence and return an NA. + Fixed bug in 'aa' model in distToNearest that was calculating distance incorrectly when normalizing by length. + Changed behavior to return nearest nonzero distance neighbor. Mutation Profiling: + Renamed calcDBClonalConsensus to collapseByClone Also, renamed argument collapseByClone to expandedDb. + Fixed a (major) bug in calcExpectedMutations. Previously, the targeting calculation was incorrect and resulted in incorrect expected mutation frequencies. Note, that this also resulted in incorrect BASELINe Selection (Sigma) values. + Changed denominator in calcObservedMutations to be based on informative (unambiguous) positions only. + Added nonTerminalOnly parameter to calcDBClonalConsensus indicating whether to consider mutations at leaves or not (defaults to false). Selection Analysis: + Updated groupBaseline. Now when regrouping a Baseline object (i.e. grouping previously grouped PDFs) weighted convolution is performed. + Added "imbalance" test statistic to the Baseline selection calculation. + Extended the Baseline Object to include binomK, binomN and binomP Similar to numbOfSeqs, each of these are a matrix. They contain binomial inputs for each sequence and region. Targeting Models: + Added `minNumMutations` parameter to createSubstitutionMatrix. This is the minimum number of observed 5-mers required for the substituion model. The substitution rate of 5-mers with fewer number of observed mutations will be inferred from other 5-mers. + Added `minNumSeqMutations` parameter to createMutabilityMatrix. This is the minimum number of mutations required in sequences containing the 5-mers of interest. The mutability of 5-mers with fewer number of observed mutations in the sequences will be inferred. + Added `returnModel` parameter to createSubstitutionMatrix. This gives user the option to return 1-mer or 5-mer model. + Added `returnSource` parameter to createMutabilityMatrix. If TRUE, the code will return a data frame indicating whether each 5-mer mutability is observed or inferred. + In createSubstitutionMatrix and createMutabilityMatrix, fixed a bug when multipleMutation is set to "ignore". + Changed inference procedure for the 5-mer substitution model. + Added inference procedure for 5-mers without enough observed mutations in the mutability model. + Fixed a bug in background 5-mer count for the RS model. + Fixed a bug in IMGT gap handling in createMutabilityMatrix. + Fixed a bug that occurs when sequences are in lower cases. Version 0.1.0: June 18, 2015 ------------------------------------------------------------------------------- Initial public release. General: + Restructured the S4 class documentation. + Fixed bug wherein example `Influenza.tab` file did not load on Mac OS X. + Added citations for `citation("shazam")` command. + Added dependency on data.table >= 1.9.4 to fix bug that occured with earlier versions of data.table. Distance Calculation: + Added a human 1-mer substitution matrix, `HS1FDistance`, based on the Yaari et al, 2013 data. + Set the `hs1f` as the default distance model for `distToNearest()`. + Added conversion of sequences to uppercase in `distToNearest()`. + Fixed a bug wherein unrecongized (including lowercase) characters would lead to silenting returning a distance of 0 to the neared neighbor. Unrecognized characters will now raise an error. Mutation Profiling: + Fixed bug in `calcDBClonalConsensus()` so that the function now works correctly when called with the argument `collapseByClone=FALSE`. + Added the `frequency` argument to `calcObservedMutations()` and `calcDBObservedMutations()`, which enables return of mutation frequencies rather the default of mutation counts. Targeting Models: + Removed `M3NModel` and all options for using said model. + Fixed bug in `createSubstitutionMatrix()` and `createMutabilityMatrix()` where IMGT gaps were not being handled. Version 0.1.0.beta-2015-05-30: May 30, 2015 ------------------------------------------------------------------------------- General: + Added more error checking. Targeting Models: + Updated the targeting model workflow to include a clonal consensus step. Version 0.1.0.beta-2015-05-11: May 11, 2015 ------------------------------------------------------------------------------- Targeting Models: + Added the `U5NModel`, which is a uniform 5-mer model. + Improvements to `plotMutability()` output. Version 0.1.0.beta-2015-05-05: May 05, 2015 ------------------------------------------------------------------------------- Prerelease for review. shazam/MD50000644000176200001440000001605214506610333012052 0ustar liggesuserseb0b205631137e06c28ea18f9f90bc27 *DESCRIPTION 27c5764599abbded5e480c688ed71eed *NAMESPACE 2f050e208eb0a4228c0c25f52abf4f4e *NEWS.md 155b9806a5b267a14e5ea332d364b720 *R/Baseline.R e7dfd25258f92ae4d6e7f6bb48006cbb *R/ConvertNumbering.R df12b5bd4073277a19f475fee9739c07 *R/Core.R f31c2828e91a7c6fedc702dcac447023 *R/Deprecated.R abe478ac2983a348d5298f0a473cdec9 *R/DistToNearest.R fc1ca93e44b98d017ecfdde4f1b57ecc *R/MutationDefinitions.R 0617de4fd6f433d6dabba742b357a307 *R/MutationProfiling.R 361f7757cb0cba262541c706ce2ce9f4 *R/RegionDefinitions.R 8c613e94ae4de36223f2fa0213fdd23f *R/RegionsExtend.R 555cc017a54d3f0f8d84b0ac71c612a9 *R/Shazam.R 865102bd921dbd0c99a38c8ad734dba8 *R/Shmulate.R dee7b4b4568450fea58ada84a0dae2c7 *R/TargetingModels.R dc2c1550661649ba66322319bb965e07 *R/kedd.R c175a455cf91007926d8b78113af8f73 *R/sysdata.rda e4ac289a5d4753b7f9ca4462eca14fb0 *README.md 7080893e02c49cd296d4424b9be55069 *build/partial.rdb d0a0f82fe6b9217e91c3c37202c65f3f *build/vignette.rds 4f3f035add5dd931a91adcfba1b27baf *data/CHARGE_MUTATIONS.rda 13597652e61fec4478f242e85d1f3ba5 *data/HH_S1F.rda a627e8ddc6ef887649482e4f51e39589 *data/HH_S5F.rda 90e5eaa6a08ef93e8b15b32df55c48f8 *data/HKL_S1F.rda 2b74df694e73995d484cec0792f36366 *data/HKL_S5F.rda 31b8bf981c581f80d98fa3cb961b40e4 *data/HYDROPATHY_MUTATIONS.rda 1d29e293a915c4d1f4706535f292329a *data/IMGT_V.rda 4713b40fb5a5c8dbbb3a553576bf8b83 *data/IMGT_VDJ.rda ee7ff985764d385373f5db509bfcaf1f *data/IMGT_VDJ_BY_REGIONS.rda 16e854af6aabf5ad793aeff99a2608ec *data/IMGT_V_BY_CODONS.rda deb4ddac9b420d3197189f31c1da30f0 *data/IMGT_V_BY_REGIONS.rda 0c8d4a1f433678c4083485d780fcfe4b *data/IMGT_V_BY_SEGMENTS.rda 914fe6b6ac51d564ef2ba1f85bb85800 *data/MK_RS1NF.rda c4a1bb3550fd6b5f81a14054add21cb0 *data/MK_RS5NF.rda 6133c0df8fe0735d20445d3c933a2df8 *data/POLARITY_MUTATIONS.rda ffaafe98d3c5d5c9b31228c6fe6cca47 *data/U5N.rda e19d67a1cd256df12aea9985917c0683 *data/VOLUME_MUTATIONS.rda f3b50c606aaca388ab4155ea3428f454 *inst/CITATION 85bda22975f3c86f80d417a5e12cfa4a *inst/doc/Baseline-Vignette.R d44e1b00d4951878d9e8feca01b4b215 *inst/doc/Baseline-Vignette.Rmd 86acf158273827d789118681d3ffdc74 *inst/doc/Baseline-Vignette.pdf 16b10a32f2314ba4c3d80e41814dc822 *inst/doc/DistToNearest-Vignette.R 845c71a27653bc52c41d69845c43e7ff *inst/doc/DistToNearest-Vignette.Rmd 74f942356b25cac336a72b5b9aafd9d4 *inst/doc/DistToNearest-Vignette.pdf 757e22ce501db41d3fd1e985ecbd91a1 *inst/doc/Mutation-Vignette.R 74d6ec5a3599249c2762cd8b1f31b958 *inst/doc/Mutation-Vignette.Rmd 4218ebcf1f6df07851622d5cac0a1dbe *inst/doc/Mutation-Vignette.pdf 1dbc6b81b597b3046d502911aa2b675c *inst/doc/Shmulate-Vignette.R 1b1b263798e3c690a45f8eac47ebf3f4 *inst/doc/Shmulate-Vignette.Rmd 39d20c8d18e57dcbde3a28d9eda83178 *inst/doc/Shmulate-Vignette.pdf 057557fea1d11f2a241e07188ecd23e7 *inst/doc/Targeting-Vignette.R 7fab2cd434427a8675437e9b0c951b60 *inst/doc/Targeting-Vignette.Rmd e4633b5db02756e61f4b0520b92fadcf *inst/doc/Targeting-Vignette.pdf ce614c872889c5489f8b5e376b1e03d7 *man/Baseline-class.Rd 4c9241edade6d91e3a06f28de4694078 *man/DensityThreshold-class.Rd b32b0ad8b0c85610fc217a14f1db81b2 *man/GmmThreshold-class.Rd d5a615ab4582a0e79e721a1ae62fcc66 *man/HH_S1F.Rd 223e7522518b67c36b628464b95a52dc *man/HH_S5F.Rd fd6b00b1452d45467fcf48c64526a794 *man/HKL_S1F.Rd 27710dfa973ae1aa2714463afd1c706a *man/HKL_S5F.Rd 47e947f7796022fe66f6bb95d06aafd1 *man/IMGT_SCHEMES.Rd 52ba6171ad460704b1d9157e9b23b923 *man/MK_RS1NF.Rd fb4f48c2b4dce0daee89ba3ce5b303cd *man/MK_RS5NF.Rd be91c489ac41ef19a5bf5c85d15de841 *man/MUTATION_SCHEMES.Rd 0784db802d5b7527b49cca1214e5343f *man/MutabilityModel-class.Rd 13787752e7a8c6d36e4580366cb8be82 *man/MutationDefinition-class.Rd b6395c818a08c84df5783fed5ab8c409 *man/RegionDefinition-class.Rd 61f1e2cccdb81bb8a0c0cce1a2480f18 *man/TargetingMatrix-class.Rd 5fb3a8e863923c3423319d03c4ca7da0 *man/TargetingModel-class.Rd 7fb9fdf190107b6241b7b226958c1861 *man/U5N.Rd 77942a00353a374d788ff8cfd45a052f *man/calcBaseline.Rd c382d48eb26974ccde0459b43aac1f7c *man/calcExpectedMutations.Rd 28671ff2fbb86a8bcc66e1ed52d0cc91 *man/calcObservedMutations.Rd e360c5f55ef91279e0c90ace525c57d0 *man/calcTargetingDistance.Rd dcfe2ba5015dd474fbae85b473e81152 *man/calculateMutability.Rd ed377986d09d9a6876aae278a6b29bc9 *man/collapseClones.Rd f27c2ed04762ffd506758c786c315569 *man/consensusSequence.Rd 3b59f03a0d4394b1d07b2a74b513753f *man/convertNumbering.Rd 4e0eb86d5e1b4fbf0253f3c292afa783 *man/createBaseline.Rd 1864d88f4828360df2e201e04f698502 *man/createMutabilityMatrix.Rd 2f17381316788a99b4c087c1d5eb4355 *man/createMutationDefinition.Rd 492ef3e940bf53f731ef9e5cba3a8653 *man/createRegionDefinition.Rd 0480e16901f4ceb4f94899df3cd13058 *man/createSubstitutionMatrix.Rd 078e1bb0aecb3764fe2861149f210dd5 *man/createTargetingMatrix.Rd 0f0e999821975d77ad1d094f8fd09369 *man/createTargetingModel.Rd 4be7651d82923aeb5f1d4b8f6f5a2e27 *man/distToNearest.Rd 6552e2b6c3c0f9d01b53fc83634e79f1 *man/editBaseline.Rd ef77804b38d00811f4fac9f0943dfec5 *man/expectedMutations.Rd 67cbb2869a1763dbc426d0e955c6881c *man/extendMutabilityMatrix.Rd ad87d67c4a882136c7afa28101a5b37c *man/extendSubstitutionMatrix.Rd 76d20cc31b9b808d505cf8e4466d05dc *man/findThreshold.Rd ab315ca57e4255b74ae75cacab6a9387 *man/groupBaseline.Rd a64599327530e7dd2e26c79dc2bf27ae *man/makeAverage1merMut.Rd d3ad96cd8466448271e018d0774ebb44 *man/makeAverage1merSub.Rd a421abd5d1b8546f15e2926a3d3d601d *man/makeDegenerate5merMut.Rd f1e35fb60180aa5226b1ca07fab136c2 *man/makeDegenerate5merSub.Rd 5fcae87eb1a59f104efe578af61271d4 *man/makeGraphDf.Rd 78ae223253f6b1a272a846a881873e7d *man/minNumMutationsTune.Rd bdc39e88e23f42333ad25601b30a6f00 *man/minNumSeqMutationsTune.Rd da8d087656261ace366c8b452411d249 *man/observedMutations.Rd c482f662702b56cc1cfa6a32da83e68c *man/plotBaselineDensity.Rd 92660620d7f16180cf0f5f997581997d *man/plotBaselineSummary.Rd cf65429261fc48ae7b596ea422238d63 *man/plotDensityThreshold.Rd 767b693dd2766bce94522103c3bb3459 *man/plotGmmThreshold.Rd dd51d0355510732690a33bea5186ace5 *man/plotMutability.Rd ddbe84750a199843ac9db662d50596a9 *man/plotSlideWindowTune.Rd 19b78b8ada15f437337cc80339f96048 *man/plotTune.Rd 4d11abe48d8f5e735225cea8bc6de2f4 *man/setRegionBoundaries.Rd d70cb61ef011a5da286c68a293b63259 *man/shazam-package.Rd e946ccbbd09fe107e00855ce1e8746f5 *man/shazam.Rd ab6c72f099c6fac7cb2c127428f58e16 *man/shmulateSeq.Rd 1b69477030e9204221b91d6e138f2259 *man/shmulateTree.Rd 4123b9d195ee930c6c2f924bf70a3295 *man/slideWindowDb.Rd 78e31167100053a36c81ad52b2362bea *man/slideWindowSeq.Rd 3e75858a9ea35d3b92bcd7a232da672e *man/slideWindowTune.Rd ce64b1e2f08ee66a801b043abaa28a73 *man/slideWindowTunePlot.Rd bfd73c6a8503f8b083a83e4dbdbe6047 *man/summarizeBaseline.Rd 16716625ffd26275f998d13c270e53cf *man/testBaseline.Rd eb562fee7fda5169a426ea340a6a0dc3 *man/writeTargetingDistance.Rd d44e1b00d4951878d9e8feca01b4b215 *vignettes/Baseline-Vignette.Rmd af30a44291d1391fb0fd7e070291f8ac *vignettes/Baseline-Vignette.md 845c71a27653bc52c41d69845c43e7ff *vignettes/DistToNearest-Vignette.Rmd 74d6ec5a3599249c2762cd8b1f31b958 *vignettes/Mutation-Vignette.Rmd 1b1b263798e3c690a45f8eac47ebf3f4 *vignettes/Shmulate-Vignette.Rmd 7fab2cd434427a8675437e9b0c951b60 *vignettes/Targeting-Vignette.Rmd shazam/inst/0000755000176200001440000000000014506573216012523 5ustar liggesusersshazam/inst/doc/0000755000176200001440000000000014506573216013270 5ustar liggesusersshazam/inst/doc/Targeting-Vignette.pdf0000644000176200001440000065424214506573216017507 0ustar liggesusers%PDF-1.5 % 9 0 obj << /Length 1699 /Filter /FlateDecode >> stream xXY6~ϯR᥃A&A[E ٖJP$NHbQ3y xpvŝOUIbX1 #x\W˫}V>5:o[mfse/4fweyS!"z s0Ƥ׬] wa~O3/*Ww fJvK5|%L0TES 5YծW;®yMݝ(\oeKAS]B4 C);i;ײ޺`U􊮶yG/lGH lxR<0 ͦ21`尲sBҸzAT2|seIPN>8 {MMC&$*A,'f2H'Pe 0\̃!שdc.#$&Pb=5*Kj9NIoCΧGZ"ӟu tLD "]G[y>"(.X4fS*{k: ϑ QKײOtڷ";7@t=g@,.{~ (yS۵Qtd6z ZP9z,Hd@&)DJ g@Qa>=z~~Ny@A:e]8؊"4SF8c"|Z3'7Db&wX.“ cLl> stream xZY~_ؼY(  bm': /NШ=uLtzS*EMO^ /-"b_;6g?|oR"̓DnLn(#%6׻_ڽWqcXzm:x`gjzGDt38URoxOcqSՈ Wj#0r,01m&aeTPlsK׷HQڒW·F,'x@MdxЉ/sf:({..ƾfCvc`y)J Y05{_;VN^rf*9So HdeﻆZ==2-8X |Ie9͢ yxJC;"ep*wۼ ƍoK%ŮSJӮwJ"/gX:*)4t, ,'L'6"IPHs +Cn|6@R9w]e;Z,i *L|"˅WqxPf9ݛdU{!88ѧc_ohw;kNh$1>ݠ$VZ @ޯhOiXPԧ1ah6pmv[7@;!-> $YH$B'gEPCTڝ3ӎOI'UGt̝)Jdޖv-zw⚫CI<ʫW!cy5P|m}5Rwwx!.,EMy-<6",U"Pi*hǡkyĬU/bk"LvmX`3 @z`3j򖉬 !J`sy. DEK99pX] H+\1wE  `6W0h|{O_!>w,P2qprf^ *] *c\^ z8A F~){fJ,Yv>)=ĩ0B+Gө|;J'9zKIhFgmFҷO#< #II0lwL`Xյ+:tb < ^8x!*I;|[5U]=p:v60~ƓAR]ҼIRHފ ;qjXRPpuKFfV_4¤_'2SA4Z?2&##Z'L?)(4e.8+gjg/Ge 'a Z%PAHRvnU똧v. E"<8*Beh>P]"z 1j@mL!:k:3Xk]UhWFu bNn"]!eFX `a^[ThG.$ua 'a8b>/  M< eK3w>ϡe@u`[ l|OhW*U5r2*0uk)ҁlLl> Wg@Y]U ?TP8!NdfQq>syQO#_q:ȔE9%++tfK XRn,j~gWnR Hw,UeVbQ;-"7֡S'A]DaXPVG4/Jf'Yѥ7m-8ж(G'8x(KjqZ|O@b/nhHъ_J愼XxoW$ϣiw})Sj) OjH0@YOE8!S" 2DZ܍DŽcp' K͖ܽGCW$*Raqtֶ,y" MPڲv.43GsoN937'~ i9:ʡVa3T*iKJm۪|\T6fbZL x%1^aVqֵI ReM:~'rC|)} endstream endobj 38 0 obj << /Length 1864 /Filter /FlateDecode >> stream xr6इ3͙z%q{I:J$S/H,۱i/$],vޝ)p}m{Z*w=`Ú򴗅^@Ű>r0O*Q_mkt`֙r0e0mW̏<(D U/@Bvv=sX.Omc*vZN'ڷگwLh6C9^xo``&#yA Sxhx y]cMyd:}s>IĿ$}xpZ骭L-C1 F2d'w=^=BЬʛ(H»@>f]98#H{}7x!,JAv:ϖ;Б.9zޖ )R/M ojPt9G -,%y8+&BTF)yi6=B?*ײ|ȴ\*NsFǕ4:3,mCHQ${kJ|L\:? "4l?l1įlg|MpĶDr({3876( 7]+mmKvEkIgu93sVm/adq*WwYO9n;p!oB 8H&+C8r+1͚e'eݷ<M] #"GGIdfu><_qVcŽmv`Ȅ_Q^ݢ xK=+qH(ȳHbTEK5&>& BkL_t/א֝@x? GPg(>vXmmx+pffпC_䙽i ET䐹BkcP4'bߔ."X!E<;P>@NS v,u]{s,9t]||ynF(I(2#v#mH9Fw1k/Mכj{>̯BB0R|6 # zիC!B:G1)97em 1oaUmOS㱓廬+@HAX<}oQ+?R/SD $lj-3%aWZsdk*TDŘ]'z3TYg*>?.$>;y^xO)G;5 cuR0*׎5"cun/V:EۛpxPy6="IB N3a:df DF+(`SyAVp[٤AX`SiRyi>樂Rxp|Qgp-3\~\#j`RU1t lt]gI*@2B;ہv $V@!.-NK`D_Z׎: c9;:A(^xԢ,2%R[lMx`-!K-Naȴ @a,pؽ(aiXLXB [/y;njxt /KTk^PSLn|JLr[? t:cB2*.63;c6t}T l<ׂ!M]vI 5;řϖ˳}Eq1"Vm뿹diLڽUg{N/DoJ4IE񌗧h-M>3A.܎nA?M`BlF;rk˝ЏuJ"j1:)n,zAaz|'-v3*珼>dzrO(y}f.rPG68اHa!)(HшtJ endstream endobj 44 0 obj << /Length 257 /Filter /FlateDecode >> stream xڝP=O0+ND;;N⁅"Cۘ4CC=S F, Kg=B 7@PTrKtq-J=9{zSrқg G)w#2Bddy^tQ* 5A<"]r!5T5<įjv8> /ExtGState << >>/ColorSpace << /sRGB 49 0 R >>>> /Length 15348 /Filter /FlateDecode >> stream x}I&qݾ]ʹj) 04&޵!RĉȩeoxCvWq*#322Fw<w/?/~_'}[ioǏ㟿? ѿ;g _tgwex;ģB<>P>?3U4W:.ɸx?}933^k{|Cgן?>|QWÛ?I峄{/R|x}cOq}Lҋi>W}~ÙMxŌxÅ"opxG8𢎯~XGSE7|:||I:ryO&<_ o[o=m=nMG޶^XMwG<uG ?}w}z Q|yςw{N*:EAȟ>Yn'L ςw5Ы'< mO:~0)~^ ς-IviXoU]όwy 1OLj~pmODcȘ>+6'zxQ_U_aXo+6'z`"(oxoO5yYnw+[7?]onu;Oogw :D}9c;T O<{3~:y=Ɨ|?1NUm|vn|_ݏ'x!?{D {8YeTv _3O!.7U1>w8}L/ c"_/AiIu?fX{u~닲G)U|}NSS<.˫=4y^ד>Sj/_gra7|'4U}:n߯ϷSzm~Ot=˯'3LwQ穗,k̙AL^KCmyxϜTL=JtiVx:HKx|a۝{Rgƻ}Ld .WeExgo}(Aef5qtx?ߟ|H?N]YxVoOz}*ӰRU=>Gz}zg ~:f8x/<+}?w07f?W}=V<3}?k WvNr'< ޗkvo; όw^4@x_^”rop>n}u|_w׊g@z./όw|6DŽ'>}'zy8Hax}#/amųDKll7W6&oV< }HKl~h_}MޮxsW_<7+iW"6v{o;oW< mO@?E]ςw=?0]s'moxU,Lu +6'zH+6'ziӶO_Yn1Hvs^ߊg{$wųHx4'< }cųHtgų}zj/]oY~kς7ɓfTy+>Gz]7fo],xfyz4{ όwvw?noGz_op" ,gƻ|N8x?3x?Fz_̡HoN;ţx1E_,1/g&bN%E;`ςw?g"x03'< }@?>Y1 >Gz_"/]_!gou}=c+>GzQ8Կ]חTgƻ`O9k< όM_GvjNc]ߒ|xV#=w/!xK渔ϊw?sO~)ϊMgxϊw#= [eWo;=OaYcw3>Gz/g/P4lxH_x]SzGx?j *?YG'4o?Y'ȷ(9o1YbHŧϊw#=җ́B/x?ҫAxz̖CK~gƻӈ|OwOx聾ldGzH_xr2g@W, ~H%jo>Gz_v:'54g 9!ߠ}g= d_,x#}ԤQ~ ς ;>o,xZ;Y?#}epó}NwaOxo?uM?Y=gV^?ɞ7< 75k~ ςOz]IZ|пyó}swz]KDoV~³}^S&jd'< }?s~ ςOzy@~³H[)~}ó}{#~jϴfx³ݿ?;?uGg2{C:޶Gzioߗ#[uWO{j G<3}/zxǟxH?;aoD\gŻ^hojxV/Jٿ%gڛc4ߌgŻ^#̚Mk=ϊ_57Ux?kVKk/)-g?ܕx?k?[Q,xV/J?%t7x#Eb}#KkoUόw;kV+Ry7?gƻ^@7x /Z,x_(;o" ςw#|2|ςekOEz VtųH֗ hŊg2K7 o&.#~H!{=?(ڠݬxFz-ӻK+O_9ƿb_όw#ƻ< ޗJ?#3 ^ ςw#4_2ZJ'< ޗJſcEEv>Gz-0xF/^[9Zw|kG<3ޗJcc gƻ=0{z+>|f\;~^x?c+ 7όw=g}Ƕ1Nx}I}_{7ʛ6'z_S'=&ooxfȑ=gƻޗ7A}vaxf}J(q3HgO?ڻu3DKsC2 g'sz+6'z_Sv R7nxf u3HKz=}gƻޗԗpՃ]x?C+1uG𴷙gƻޗJmeųDK!ٿPw7x~yꝮx< mO[Kno\,x?`o57<3}X_ߕ|3HIoxf}au~߰Yn1 O-+6'z_R_V< mO'z >GzZ2tgςw#/HL.ߺYŞm7xi*C}oj7<3mOX6!//:'< mO@ſޭMV< mOXc_,x?}nO.X+?Y//'{׊gCooN {ۊg_k|gJe\,x?}nuK|x1ǿ}!uų1ſb,#9_w? \Koxf!zųLqxroG7_xw?~ .gz _o|ϊw#=ߴ| }-V<+}H|&-dųHŐin8<+}HDmF+>Gz%pHϊw#=G<品|ϊw#=G|,xV~ A W<+}H_ySgŻ#IvxV~ioh<+}HA/oYGg|jΞoYGϫB?|ϊ ;=~_gų}Nhg^Sz +[[[OVO֛՛Xx,ʰG<+޷Gz??x/?zkzςw#~+K_^^o|ųH{w[{ς?׿כ_,x?k^Y'o x?Fz-`Ep3}?0`I7q3ݏ֏u3}0?aQ7Yz[~++d zςw#pW< ޗ {#~C+~HOnxf/?{'ƓW7nxf#oxf/{/g7nxfֽ όew܊g^n< ޗ zC~7<3^-x_'/oMx?Fz-\qx_'Oox?k_+zd-z~H~w<#ޗ/ {c~7<3^o-?O_3ߴ~o?OZo}G3}3?gq7gĻ^mx_"ko~cxf۟*1Nv7 Ç_?<ڿ.>YO_~ מ?Oe(EIqʅzUOXz1C9 Лߚ%V`޷uFy[N(}k+3ʹ}}+J05eVG줽o OV/֪@_H#s~zKkU4M[5Uږ/:nt[ű)+ĭ??>$R 8qrTǸ"ٸ inq$;SaLf(IO BJ[{W'FQXI$ILYTh*u D٫jˡ$1@r\é߭ bA VX:;d r1|'2)D㹰%on!B e)cZ œ53S}.Avҧ(oU,L.t@1 MӘO,_'1Ogq'hBGUD,+ PSa6_./*|7z8z*oHF^6xWY@+ ^+wUKيIb\2| mAMӘOd,@Lfa1q'\np\Xx;.ΒEL%@Ea$l|ȜZwqpb~|"Ԥk CQ3|B4l_|B\]V2.LNixADHwP0 I&:z;U[%aR7kVdG|17ЧّG[\'7lf8MZ?ltپ;HNA'LJ dD{G[:dbʇ~=2cETc'N)|rڸMMQ$T' =!$uJ e,ov41 v*r,D /c^*If.uBEX\ۚ'&݂ɌF DȔ쬈N.H)3RH{Pb3NDk0FV`̙(5Ul!oB -Sn¡ΉܦjZ" HL L^}mipF܈/ S!T&] VD,A|zvF%ײZR]~[4%q^bTÆ{=3"djjo*OcΎOƨ%E\;8lFCNdm]y֒P_fԱd3~=k+bK{B3baIozM;.J1#b ׸&aFČy`F 2䜆޲g[NTr=;0owde&9712O↓xsr57JEfP]VqZ{>O:9*Rۣ/vwZ#%'d$^b '%-ɱ%k*9rɘ,2D-Y *HQP-μ]dM'p͂ sچ< q,& :l\h@9`Q 6ƞIjs ȊF146g\MV Kp!Ң(,@wZ( WLnrj릾!;fdkn uI0MMc>MB)vny#F.qZzrOKt1HdbJZ4M$֦@ş_m~/v{*dJg &x{uЯYn Kj%|}؈Yp1[i0)(`'h皓F23Чpe!0Z҈ӹ`h+GTѶ˪=pi~'ln7g'cX;nd串ƽ |w7i'<2U` o34'2f|Bv{Ӯ\JxgFddb(͞a wzsb*[AB& i'DVi'۪%jvrfMcyhy2+l]fuj|f12w0]kViͺ'Neڲ8]+-[k 4+4RXK׶X==F=kߨcX o68|E#]nO`Z<'Q4 zTM$E^c6: bZ]~gr1~EOKQs)s{؝ȿ=c]6q^2Eގv1kr0E 7H<bMaA޳;qhCzSdD﹄grI̖ dFfչ̂'<\1b jvǨM[xzgzQI^l.O]wtfC&U2wr[ˎ+sS 9Ct lwNRl"d oh4ŕvKnqEjȅ["bE79û%qYԸza)@ىYP,ѢYJإ(q@b \&BKӲ{'qm_z|C8=&-^ZAqS!'S+-=UVߞOM3%\kiŞ힝 C4-1Xt7iz{  ?=H<l Ryd kAx2PV}"jnr^m&q̻"zR, 2 o Y|_LU*6/tفxEw/?}sfjTm<1,Ï_o~_?<K":G/>~_?|%E? endstream endobj 51 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 55 0 obj << /Length 305 /Filter /FlateDecode >> stream xڝQN@z,la^B4`M=в5$PCa-4^4$o2} Bxrb'L$lcZ$JᤵwL22"#9>+$L4os/L5Mi` !/ſZJ(?3{qMѹn{׵;w6ǾTu՟{[z?< rWT !a+K tZk78$ Y[MW9\xjX\cX-.á?[gXhXR`*N\js{_ endstream endobj 41 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Targeting-Vignette_files/figure-latex/unnamed-chunk-8-2.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 57 0 R /BBox [0 0 504 540] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 58 0 R/F3 59 0 R>> /ExtGState << >>/ColorSpace << /sRGB 60 0 R >>>> /Length 15227 /Filter /FlateDecode >> stream x}ˮ,q|w$,(@BA"amwX>7">~>8>Ý3q_ۯ_տۿ????>~WoNOgx|Y|xTѧʇg:?^|>>SEs?~y:>>g_cA_N| g} A?dž~ڏ 72Oa>|07Cg8:3럘oe~:17\XO?}ოu|ub}&SE7w//L IֿMx^ o[o:um*?z^m|zcnߝJ?ru\Od+D%1D_YY7xwWWNB<@ ~#?oxkWwxu)~^S^[Ұyvxf 9>J74?F0cx?{yA!ѝ+'zyaxF~|Yn?Λςw[.v+yY>;77<3mO^v߮xWz.@_]Lԗ3ܡQ GԿϣ{}WP?9?,G1gk'd8a 8jg߫1]y/+ꏜqˌ'?dٗAR}5{p?uḁqw ~kwN^o?:n75+&G]O?ϴ">_aL}}}qݏW[( ?JW>_ʦ0Ы xb]WNj'_}>`Kߏ>ɍOi_gr:1xs?{JW4oj>˲ޮ'HcC]Fjpبgƻ}?M@nMKS}xg^.w{V}8`^x?{ .~X#y;< }H%Y_<_k 'z|6}vrςw}: A8 'yg>UP|~߯p>o,x?{>Ra~ 1g+'zu:Р'zo`g!óݕL=c gz/Y#~nxfOkxhߊgƻC3ݷx_5G@G7gƻo5Sjwxf?_7,M'zMQmCV<3އk<ۓE xf^{-53}Sz k 3όw[V< ާ ҃}vpV<3}2z?.O|7 O`?q_:v3^b;ðCEۊg-Kq;[Y,x#vųݖD}YFz+b?l'}Yn?sAX,x?;i7}kųݖD?ԿaXh]\,x?}Ƴ4EM_,x?H*_ϊg-+y+'zqsoςw_#=G8,x?/cųݗH˿Y>l=5fZ,x?t5ߊgtΓfW< }z6{go_'M7x?kOtoX,x^'-7<3އ_P/t3FzM?߶Y>{nVx7.o7gƻ~"u1%#3'O#=OǘI{gƻ_?{}6u, +g/=c3ݗHi'1+w+}H[/REnųݷh )cOzЃ~.,x?Fzcn0+'z/5Q7<3}hFc.V}y3ݗH%myTa[<Ҋgƻo}Ÿ_ޏ1ςw?gF-2QЏ~~HcS,G=A8YFzy8Ńx?ңͿ% Y-N%=%YS2g όw_#=Cj?_3gƻ5ң-o0z!}=wxO:φKawGwN-޲Y>@?brx|/}fyxf1!8A[ [<3}^_"tFҷ~H|ͩ|xςw[=ƿw̟ #ó}齘rr@g3{1>,=̌GzEkxςw_#=/!/]_!g/ou}=c+GzQ8Կ]ח_όw_#=XAxN%Ǻ%w~xV#=w/7xKÔ ~Ha%$K%S<+'_S_<+}++oUy~}~bкx?#}/8)d<+}@ץ|ϊw_#=/xH8Xe7xV> x?DVη(o1YbHϊw_#=җ͗DD/W׋ -җόw[=җ;O#{|g-F=g-c}֟$q],x?#}s$蝬Rwx遾S%7< }H?ނGzT\EAGzH_xr2g/AW, mig/K>ՒdM7< }H?1tJOi> ςw_#=ACAQ ς;=/H~}"NQ{u7< 'E};h߼Y>埵~;Y>?2%}epó}"NwaOxO?/SySgD^?8o뿟o,x↓|og ς?i~͚_zó}z^֯1}#o,x^Rf<[Y>H)K}4;< }?s~ ςOz@~g/^%GRgGz՞i:xg}mz>g0G'I"=YP@?4bų}Zo7z-~H!{=?(ڠջYZwoV<3އJſ"s[<3}Z/#Z,x*/x9_xxo2R6ؿó}Z;Z_Tm9ߎxf#2k=0~|%3ݏ- όak/s7xGz-ӭz\+0Ciςw?k򿤬嗉~,h3}Zƌbόw?k0f[=όak(q Rv3ݗHJ_- FO_1fk!1CPg0Wg՛sAi;~H{g̷|g0[w|sz?~@g0_遾b'4!.ߔ+Gzpañnz'z/oqWݍ όw[I_E;ooxfȑ;gƻ/KꛠVo0W<3}^췒'Jox?c5ox?{I}/MA\,x?g-K}?A } όw[=?:nxf齤I7oxf齤?gƻ/_a? 7<3mO^+ςw[C$ǿx_/xik_ςw_#=gYėZg/skW_W< }S+_jk?MGzo|)c όwW3=?_<[xV>O.W<+]@pk<ĂgŻ+C|[gŻ/goZ>i+Gz߳|&-dųݗHŐin8<+}HDmF+Gz%pHϊw_#=G<品|ϊw_#=G|,xV~ A W<+}H_ySgŻ/#IvxV~ioh<+}H^L=xų}"NhԢߝ=zų}"N%WɅ~+wzJ?|ΊgD^hgD^S0~[ϊ''aMxVO_Ne#o?iLߴzS+Gz]zZ~d S{{ꍭxVS[[x'7܊gŻ/^[=[?ϊ= ?\x?VQ-gc͞_տ\x?VsǷY>YO'jolOoxfߑ^jom_oxfړ{[77<3}H{[?όUBLCY?3-g??׿׿^,x?k^{GY>OoW< }[_,x{w{{{ςw_#~+Cx όw?c?z[~ 7<3އIz ƛ~7<3^?nXW<3އ?z~#+Gz-aUﷲY>_O0ެY,x?kwų}0?ba7YZgxog0b_O2߬Z_{~3}2?fa7|3<#އ3I{k~ox'ϙo3ݗH۶~ox!ko~coxA?Tbdv~w}<Œ#^/?~˷~W<UR\zUߣXz?1gC9 ЛsytoEݬbޛ+\I~*J{s-beFpQ97Z'!Ijbۛkʬ,I{sAǯVϟk[QN)~ϟg$~\)nBدҶ\+kݴ\DCrsSVRǿ}sB)88~A!.5d&i*%:o@$ΐ.}x1uQ,"*8J4T&c\k+EXFœpǣ!,IOtQ&xuCxij+HM(/L$^=TmBEp9#L Is":+GqH=0]]eETJ\~}p:G}:#z5^`mluMI|1j*WC4c"pěNq$sޢ8dCWй1f{eY,u g b|X N#td)YB#4 *%j>rs\YbZu#8Bb q0< S=#4GgCG|!KRWxI#G+V9xf}" GU!^>WAG i,-} ۷ru' (GŮ {Kf V]sL@5 8TfuhkTabApr$}\#UY,! ``ete# ַ8B|q8*E4qN8B V]A8-i#J*&i8V dሖ\Dp)2py(xl3\#AC,y`#\3E-%$O"Y_!J5"8 ,ٳHUt{g8YcH"8=BX׊d⤵5X5^ F#4f8+)w zΒ^p$XQ\!yŠF/-35[Ók$ت?&}f 3 uSc#ZVO!:qu>d2$GT4[]^ϙ:CH xţ𨹔8`#:)Vpkx?TtGCMDX(X2c{؟b8X`*TN8p% vy욣k"m} +rb;RWZKK(8f\ωp]t A !9Q zx/shSnpYt:ZCW;'2r;bŇX2uZ$̓p}s"5rb(Y9TW5 uɊ#:Œqg=OBGx\P,%!xk$3풬؛/d1d:[kD% >O0\H(8y[Ku"OHTC9Xw9C=%݄rnW#+FUgNx6ƌ+\kw$`y\hlY4h@\+v;aWGTF)*Е}/]- ܡ$ϓS ?2 %7AD2>JP9x%ҋ:;4d /갇5EB=,i-&ڡ9\۹pQWxs\΃#sH.e~#ڢDuX+)u1Z8*xWk2?Q?:6R{gKwMh)8XdFonO " ٪.VdqH;/ }5P z܅IxB?քwǑ)T{mM=Y)1aH/I _< ø*vkZީl-8[2$&[R8*㏡͘'f<6.fdv wOlm<kKyhL׆6m=y0O)i\UOa%-^N:HtRC^z]YKnIC_&_}(rp$ z oh 0K&ȳ}Cg\)Q])!'BxU8gTJf-.YrV ).$_to< _/9?9i!D'pcKDjUCߴl2)N]L0: υGk+n C%]` ]%8q3̗5vʴs#K u329myu'.d" N}Gkׄuڅ7 P׋#wl"5ӣ -d :UjZQDS g!/LAw QwaUK N0eDn)=v# _d_*$03'Jv7xZ1wlIsWY_"oX $_wyfȁpvHIxw.Y _G\!tﳽu|3݂Rly4#K~1u6=$u͟KKbl!M A׊RHA 5Ep/ %(jLjk9klpK44ckHk*} diBŁ|r <(\/Y(gxڎԉI t KV "SOHX2 0tX)gfI{EA@[B**%) .O5!j"aEnMlueK'`[bJP9MQ GΜF:EB/y FAˠf ZbZ$@^֠>8&G4FM2j J^^oV3ƚs&`ٴ޳)٩Ap%U&Mx (y&zA %2YP1g;̗j4;# BAWm6|cM)tH,13#,=SOl?՟&pPB &~̒ 2NxBk<OX^ccCDNNq<ɱ<ot;9i\Ǽq-5Pm OۨDo8)LG#mf#):)hqjڡ'P7m]Q#$^-f P-1Μ!4u }rRU٠:s/)Yzb `-랯&6 E ޶mB:´AXV@pf+YC,vKu0ߟr],]=>t Gbc嬅Vh\wZ4ٺ43R$brfl "U+K$Ѵ2IZk$sTKfW%mR%ڣ#% .6 -jְlw#]d3yGu? 2fxDH^ڑhRnhq>jJ">^lA;kj9kqF@فYո"?za^pvfx݉ݶ9k(V)/<_gk <]*R;>[Qaßvf{}'~id8bw˼弝^_q'Ihrl0s*=l Z$w7ckp[uVw+rM L8xf~r$~ۙtׇ`3$TBqQ.q5 %vdƩyjf*7α4OhQ&miDD5S IeHu]S ]zEWAVkZi2ʵސ:9kLFA=hB!)`ziz^aU ɇbړcG{?8@Uc|B_%ى`94cy1۪b` }YJu}{c2#61?W#}E}wg1?!;M<Ж]=hjloyOjylz6hJfbKjyCѪhVPY+;.Zv k,#[d JV*ʓS=:>aKaVffa'.cVEĨLie3/䵳5LX~f. os>.V.=x )3m] ."#f$!K"tFXx^0X{KzZ' H`pԋ/C"7`o)=3cF8ϣuP&ѥi9qUکNZP6mzR2*c 5.t(hCnfSS`Ve_5b]vaG5ZSHrץN5J֡Fe n(1󝸓Sr4HL(+qZ%IXXXӫ4TX "(zZBX:T4v~mϓ}N?Gqq[TzO$cNJo{Ů f7cҌ٩~<FuϘѩg x^I ˮC sI\WT x47h1n,Ig̢97v3e Ι:Qj:uC^hJ#L);z8F* MԜ%B\r{H]񿆰wjӭ{J'xǹ*wxIk⥘0 P/ x.2ps2\՝JK%V*N #oJ+1L܎bcfNtS=CMZx\:VfqDfxFw, D=Xt4]Xk2XWqjظ!giICU[@C.cy.y  sb&{&:&*!<] H΃Q-hhα@;\NAxuk xrV3!J-@Q:i:{4Ղ&ݥ\Ӽ7J733 =3'38E3Hw ;514N{E& IM'xלBhµ*?Z,kGQ "X!Ŕ}ap[019&&3s!Ź,z9ΜaN&`K&˛Kt%314ڂu)b6JM^Sq'n*|r!P rܺ] fHTp TZ_:n= XY4U0y2x{y1[ԽbdSu:kkߧ,0r1=g~|hݬ) 8# ̃^peu|n™Qb牆j~'e4n Yb :\;pdljpeܷԟ E*= n`;9ABcM?7?%R~Stq{/O?˺%?~/??~OOP endstream endobj 62 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 66 0 obj << /Length 273 /Filter /FlateDecode >> stream xڝRN0+V9lwHC q 8M@mz8M*.T֬vf1GW\b, ;B+9:=4n/B@ș/kLQ1HE; -Yޛ֠anmXVu.J(ˡl^&ȼJvUmWjJ(t?|1AG̿=l]]\?vs:D|qBGڐT%I%Y IfJ endstream endobj 52 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Targeting-Vignette_files/figure-latex/unnamed-chunk-9-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 69 0 R /BBox [0 0 504 324] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 70 0 R/F3 71 0 R>> /ExtGState << >>/ColorSpace << /sRGB 72 0 R >>>> /Length 4336 /Filter /FlateDecode >> stream xM{LP˱6 rHSH/$rT#O-=^rrpHao/A{o}'S?dz_}dWiս>>/'ExB<7Sg[ :Wz)zz:U̇zY1(*Os*qBA -ȵCBS )v_S(&|5R-H0JQ (+EgaqEQvЄeҶpI0Ц%&r?b66U$~F%*G4 h &adQh#C2ڪL4)S#Q2N8"3ap2V%.N p 1(i̒IpHL>!-;'*&pNy -1[XmfJb /1V(ZwJC3 Vc"jF ,jF$%jF,jtYS]UZr{jy[ouv݉8yvؖI BqϾ\?EK{k$jkz|ByϾȸKp_=6w4$7\daN8ˑo>~ >rUN[ۘ/\'l Qku:Au:2x:aA\H֏\G\'uzRI+A{`޷uT}+#N`q=rxkDOr'w6j 6XO: iMb`=C3>iCs,Ǥϟ&E'g> q[}kwZ>XϚkgkC ~=Wbywdr۬q>.ZXr'shߘM? 6QWx#|xCw 7"/:M bU =e#L n5j 8jM][z`[rosRU9'<_q[(8UwjP nˍ\"h+4q/ӵ׶j ?&PȮZm-7;Pc/yWomؔy#C 8Bn^0[mk\k![rt {9C\;ڻqL =1]jjȹz'؉ Rvbوi+ZXպFD;1 Nlӑ;1CHj'xjĶN uq87Ρ̕9Ҁd5;q8[D-"foo¿YlS;جÿE,ڛoqL7vhٵج7jk!;p8[D-"ئ7DZ"6+'oq෈8[D=mq_ El/o9%#maߢFGⰯ> Lm>|oUNM8[U*V&T3 ~ Z5k4_Uj}oU*VG|Zp8[Uj@N`oUΞv [ݒ<`i~P E$B=ȸ򻧺3D{mC=n9Cz6ԃ}䶡YHzD۬>s9F-z!w1ñzhD>ho.ط-6'qER,ˡO}CCۜR\n1t$/Ar|#E8[UFUm p*V}p෪8[oQq췪 WG[V18[D-"nۢE"hnq07/LTl!K=V&ЬF5qe՛0M՛0n)&[ c /geo¸dktzM{geq|%;KqKΡds2V8.C>WrvM'84Al$ј\.@~RǑ\~>aC!{V3Y 8[ 8 8[ 8[@͠`~rhh^\{6aL8[D-"f1hn /"1)E"`nq07rcE$8XvG?TsI0ǫ)6$~߂oN?~?Ϗ?ΣkOy}hov4|ʻhTxf9"}=j0(2NS+e/nQ@(sznP) #XùG h)\FDX:q#q 0נkXݏ._c) ^ƀ Ox>6l+z8qfZeO0S{@~ӛBI x;]٫N:|ZbEo'N:prW\x;yWY\x;fvr}IoANĞ#}]ִ+8?~}Nl:M:νMǟڝӥ>Be0 \ ~nHW&~-(]lj3lrH<}6ε%vyjwo1!O>m=sų~OtͷޯZ;k}ջ?>ym/g?WL endstream endobj 74 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 63 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Targeting-Vignette_files/figure-latex/unnamed-chunk-9-2.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 75 0 R /BBox [0 0 504 324] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 76 0 R/F3 77 0 R>> /ExtGState << >>/ColorSpace << /sRGB 78 0 R >>>> /Length 4402 /Filter /FlateDecode >> stream xˮ؀p$ Ǒ v[2}XdO9UjUsJ*,֘foM+=?om_n?}G?t5zoi6bŴӽ৾l/ Ai٢l|!ne%j峭 fo|Kp17UIb،QWSַNrkH _[C@r퐐ܩؽPG[P>M09x4RmH0JQ (+E+0"(;xepI0NK0**㌊Q}E8S}E8W0%U"FFewqHF[qJz@81mF'é(/L=΄XD1^y]ap2QigLRp(NAw-䌵*o S<36-IV9Pdb|P}[xr2gU)ԫw*+2W1"BPAdbBRF߅|D8hwѩDw1)-2i11$Rȴ$40Nɩ(3oIAYmp 2rm[S/QYfޒ 2.ed-9,"̙p*Zi+N9yKi'Jq$l'Z~մ8'"jJp:j'Zpl'CI,Nֲ -f38JqJ;kh=Dd)IҚ6-js8z"j89Vj8Q6j38yZfp2Ӫ6SRIyJfq hU)Ъ6STm'Jq*E8EZfpV)U)Ҫ6js8Q6SD N+-j8Q6j38eZfpʔ)U)2OÉV92P6SUmB NRy(UljV&4js89pNNӪ6js8Q6Tmz dhUeD3D NV,j8YJfqɬ%':1é"Um'G NRYj8Ѫ6j38yCd-8yJfq^'OND,NV#r2.Ъ6j38E:W)R6SUmHNZqTmdwVj8%JqUm'Zfpt6SDSUmTwpTmB NV'X j8%e]U9yMN^S6״8'iUÉpDUm'c,Nfp2DNd)UR6udUmDnpTm'G NVj8@d-8Ѫ6S&8yJfqiUӪ6D,NAYK NV#28JqUm'ZpTmH,NV'"ᔉs8ON{=OPSϤs$8l, N=_ISs^$8 N=oBS,%8iN}SӾ(i_%ב$8 N}-BSW%8u=[SW|%8uMTSW %8u] iI%yk@žP{^ٻۻ^|eq>}Va~PL:ۑ>iHXGV|mBi<']}r>C'`|>0O5'6D>] G>sF%OH1}ztI+ARz`:vӶr;t&#>H :'.j@l Ӷ]Gljl @L#th[ٍ#7e4Vi7Z1;f R#Dt5f0cdN ֿ ;)xPz[Ė!'p kJ juQk Z#=nF hѴoEتaڏoGO[apk#eC&x '5?^<<12=c <(Xަ}ZXzھFPQƖypyS[e0Axa^6N@sG&k:n֖0mtZ=(53^| WXzN㜦qsW!_?k-(`F-3:X+P?fL[ⴡT_52cp0oHı!ÚPk h( ZE ^JIG[C5:A ygkMvJ̸gqFW %md5k6[mPaa}ޚ5FMk6m45Fk5[ji]wdm7FPv ͩA~غ58ï5ukU|Xb,fkLuܑor|44Jh K 'r k9ti]6Y[a۰kT<Y,MָkiWޅk65`[m95'"fh Aǂi &Ƭ5Q h-9 PpF% nx^#ز`s5[ke=[kPJa{s6[řMMƄO^FӒO5Zk6[hmP-a= 9lvsmH{h ,6ZC3Td2[C w{5sk5zxHMۼ5_#h`_vX5k6N<'ꐬaP/*b/B0Tas48iɺkd *Z}@7-: 尮lmsx\5^-T|5d[Dy6A6[5f\ɸaq&MB̵]YcC!>ui(Qyݕ5}-aUCJ@25P*; ^nHtv(0ݛ5`wk8GvXPD#b;9{kq&#Ar`FkNbZ a]ٺ_<*{Zo50*kǮ!Gq^ڡJ5'۰ie}҉C" nvk#n9sp+}F'Ys[rnMk6[2ٞk6[ò62Y؎qX;\mW!k`3e4fml>Kf{]>\bCc["u?}P~{v~`߾~荷=Iisv@`BrXE맲 a)vϢ/g6P'5fqh)4PjXq)4 2D@'Hc*b'c1xuFf >1ۀ9ceÉ]O4XM}p穾bkuV6['ٹo闟k:NpŃ5o y:je7\M}3j ?̉gi"Bly?VgϾ|?l^S3 7~y/۳_* endstream endobj 80 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 83 0 obj << /Length 824 /Filter /FlateDecode >> stream xڍUK6 Ћ D _z=5m^@P -ɁkQڒt6!)[Zl^$f#Gț;^[9J@AQ8SJfWʦ*vGF{Lb?!'mekgcǬ2AIF8Vۖ*ՐYMN&^=oa|ؕ5њ7X6Ur!8I_Ajlas;о'aTt~v0Ι>^S%*'Q>-xw'VʌgX@{?>ɳ :pK$ZAq467 >DΩ9:tP5}E.xx1upFg~/hx"wDx!*>3&.>Ӕ us"UXQ/AwJo舃V :NRK'J~ە on)5FJ.i3Pmo1A F@ɧXrSśABs"zۮgVi5.n ɩh$)+h"f6 endstream endobj 107 0 obj << /Length1 2008 /Length2 22667 /Length3 0 /Length 23904 /Filter /FlateDecode >> stream xڴyst۶uAbضttm۶ٱձӱysx~Fa.[{]"%W05821pem lhmLt ,B@'s[a'  g`b`%mNc;@dndPmh ?@Ss Gӟ̴*HYں:Z lt2tY[9`432ؚ%E%%Ga%g;;[EHIYE , ,T<*m>7*|IQP֐as F F߭}8ZE0sr㢧wuu3uvtu0?e3sG%hk06t2]ϒ͍6?I;?FawWcpSp#4fJK m66FNNΎlw1 B8d/m]ʴ< \{ lc6yF6NWḼwf6dd%DEi?gC+c1:'7p09Y "1?dN_uoɟ;ѫؘ;%'o) nFfR3b >+ӿc[e;~t1ߐo\{?_{bw%P!2NnZ qW:A@قn,lZ&N#+;0{G_j9@vyֈERcPHT)$)'i9WuԩvLm" __oY8wM:w4coo,U:4%N"#ɬ\"֘V|رg{c$;U"viZkcvT#jtA2լ~n$]O5q 1rEREgw%qOO1y#ZE<'vugՆ ,iKYJXiwDpLʣr7FO$ง =13BpJJ'^C ! 0ɰUk]Q/B2 ew,N$ ( -Ks(@n0d>7KL˙m{Yp7lyhC?hG0 \J+]M.%=&"uOfaAdr93k GTkVΧg )|u; 2jc9ZО ֜RY㟆o낢39+Ń?iRTzPM#䤢 7 2J2غXɥse e?hE=L)qtg$) MV[ H[aftkNUd"xI;"L#^E7y"4ʯT;JG] y൛aϦ͈ĨqPiyЦdKt_*,7g浥Yia)Eȅ VquyZE[>s*iw3DL)l#d ηt88xY1Q+#(  ϟ7Dِh~ ;sS1H2Zc}FFusDvCƷd"4iW^\wC8C<.dڷBQU@z㌀[GC^Z48҂whREr\S1><{p$p5|mڸ9dwBUv"Dͼ vavOW"!yK Q3erS id}'T`?ʃ+qQ3$I;ۑR|sŌnɉynI.Nu)uOvKS"w!g,*-כ(U2f=Hh<Dgר?[PoQ9U wA`B2z٧6yJw{('o`S'2dEuSΥMV!ǂì^JWe-^oa{TYlh  >UCnqFbZmz"ڲ62w706`Dn&,?MXcPPqd=|u]&'h:R ~Q~} ECG3dܦJ3^pmY}`GY] 6(4s/YQ|B%d<B zdF^]n3StV$` RIWf: )IRG\)YhB4O'S#mE0U_ɑ C+fOBӁVIC_ i` pV59S}mdSjD6˫Ť{#P] 6G  ᓴ-ۘ@-wj$z0$l.1jk977P@&ˆRhF49Oz?|I2 ⬄f BTZk G^z86:,YwKDz^<;(hm KN6DRLbQ䭿ίNf50~Q#U";1ZUMj{~Z #(P|2׋.ٖVy+Z: :~q)zY!lz6-Ё5W6.-c|oT1s&_FJHGwchs N$fY!-Fg` ٻ+L;;=lHA/yqʅgI]l9Bh!ӯv!Oڷtq" ++=~\zU^ܑ2nxl\^/_$o;P-.x`%[kW pe$S0p˅i% AoY | l DEg۪R\۔aA'|V{/dž8GT-|E p0NMEۥӖ$tprXh TrBF+LtlZ -$*%)J!:o?xUװ+[e?#s\:/Jn6$q4S\ëNo0DH~۔A=Qf7] 7~K`YH&>"0yyG[Bo>|۵w!^QŴ @If‚} 4x2e֚S <0p*!'Uv(ҬH+3bp;^k>^.Ys 0.V!Zp*}G￳x[KWnvV0ւ5]T$rpL^ ^{4fh;QW lVu <NEvj pfRn<*MAͥN䄿_}IY ? \4*>FtVn՜)УD%^.M6/"MWH394br4 P8 +35l;WAIQMuL;_QGgT9w_'BrD彐|w!M}Rݻ9j8>_4ߡ:ySF%Y9T4TE˫Nn 7υt@Y :=./A/1t~T\~Icjܨ Y⇭  nP/ XaV,-{y5GֽvCK_\i;?g '7 N*'z&%Y9FQOM#X\)z :yhī h'(ܺvy?8׌!z-={;.@yOD0%>=Y,щΝ(gjq;ԒI'p`17Qoޟ~01m?Fi .9G?RI{;ceLٗXX#V0X_tN YWVzi{mR8 C9ʑSR{"޹Gt8Kc[\I@8=9_e$Lv/roŴ"si@z ])Q.%QzTBh4(4 ؊Hz$TWb0y䜥<' gAʣ{R,{0lČcDPa7wXګN?i?o9Μ̇Z($مD4k6&Yt/󜞮x v_dz]@iŖ[^"I a}Vg,u%sfB)8$JQO:k[^y`4Pwx &94܍Sy|v֏q Z:hBJ_؏}7F_SǷ_~XMZgLp}$8rxX)0)G?H3acRlU9cbhGf6Wf}D<$$:ucplG`ŭs.<*nOhҊImh1,c|5qlQxG| ~Q4725ef. ZCRv-8F'~]J7ߋX9$-^j3C̓.{T^&B^śocZbP'!b'$5l갑RXޛ4hahum*p>tzuA Ru!!¶{ C[@G~˻]Nk2;-|~"J="'\W(9Uw+*pP]2J/%.·#b1xk?TXL)>G.0}m+m‹pplḏa&ԠI:90;/z$:(o{&Z4*p1@A+ux|.@|m JzFu)wd/-q>@ӋFV&`?y#KVYK0˲jIW?;'Jɫ$΃0i_In"LSK7pa}gz^)44>|,AYn#qdeX s|hYKTz0S/yKXĽyc=2ޒ5KYzQ\[-smN $d^ִ2IU*_idrLU%$1Nm:㱦MPǕiQ#Bu_M2LsW=xGc~M@!{ n f?o'є\Ko(dƠe~}zz yPئVXZJ^8%]s2F#Yeͺ> sQ.LlFp{R<+Bo.4(M.n# +KJse$fB|Зs7kRvnSvd9v 都t9H/c~,R`H< 5 ƩȮ"xkh@:ըc[2!`A//G-6B6ʋaF"ʻA%aBO`IإHN$H{G,Va%JmÛ9F4icy(L]focr~GOGc`?GC=[nWچ!l1Mpy<K㍲w1OpքO/IĉBTNB,/{]`^4킳4:aMk~*8upqQ#5pTyȳC@v⻺1زVN7:usvИ4355Hp6{RG!X@lr&8 &_eȻ^ry^ vH3G{I({X DzI}@^~ri$; &ޱdU$HlZZq S՝ O_+2ʰr}7S@ V?9ġ-{9LA%Et>_ǮhxUsR-0\mD Nj(qN5Vt݄< R ~#_=Uef_ȶYXRE_#XL@ bj㙗[%<#0=br(LTGg2X`WtVV!u|c}ÍG|F>{^-Ak/jE-^ S[3FJV^WNEVY*M 8e^dϙs-xΝ f,jp`.s, V5J}OE<:5G p+T_0<>W'bI4U.BQ,Z҆FpODkhtp9lKXy" 4f=#}Ѩ E \sB _p0]θsq(n% ߳j %9 y1z Q{}cX^~!8y~k}DDyI 7/FB~eGiL !o#Ʈ.(Td2ۡ9OᏡnO;ʘǁ딇#):#5'*T<R Lq$-` _q1IC'o&V=mJ W jD^L@FY̾rFe[p SP`ՖvafV [eaJ8MF|QBmmr|%rm_qʰćn9Qƻ®9!J+(H sr,ڗe^3g~5]n)s _[SO[רx(24$f<7 U/7RAlchO<:p r<+GqHvA MR س S oP?bg-t@_#"OHs0(.l`<"{ʢa H]:ӐAoڨTkVHA.^@Hkկ~ yXgn2(z6a<ߤ*uyI`W2|8F) DKOڨ *! fj@jVx277+ھDgRG(R)^_VBo0!}.WTuHz˄hWp:n_ͲnY /=*>b$AˑT  ,5^?nuL&z*g B+ M b^_T̊ uWXDw.Cu \WSSE m%aAc!QٔFPqj01\&TzF[l0'pasZql:sA#H+O'6fcD #k(I/gY({₧ Fᦢ~>8d^S-f4ަ:X?p~%KI6r )?]hHR(PEdxaWS ITݨa&7Mɔle8p-[Lj,v87I_>{Ku7['4ͫ= Y<^N}Sء{&P0t)\m^l^ɸxc\Q8 '-nZǯӒMG]R6Y1 牲[rj%se@U冇*Mxp. Zdmnw,Ow>(ߪ{GgTqT`wEKfsrW5zFqID)̧ UfL|gݞތ \ a^n;0[P LPtszض[@d:f7c{.ӗQ/ohM~E@s9 _\uvTٻdYceN=n@u(-ȝծCc>?[y5P(lgJAN+@a)TMkךsԍ9Gz<Ϛ;uIlMcSdM MSfޤ76Ö+A~-wPsDnb͇#Ә06~6!3ŧPj gˮ [H+pZ+..۳.n4!m_M~Izۊs:oOA8!H;"zIWYZ5TЀf*qi ny-=i.9iJ7wNWhD[DݔK<+a{?iy ?Dѫuv'~bJҡ8 HL*CS`ԖBk8L뤕"Qa륨j!T҉^WIf9#>MCA`}4ף\8vLKJR7} -߲V Dz7U6-٫ƟՔA!Η[la]Mv 8A;%R_L%u*orkwbXGju7&Gy>Rխ+n(cCc^Te:ہWZJe{ ,y>8Fg3~wNۗ:"Z:Skv&9u+8 f 7+ӢacbëqL8"VI`cp]M߲ԏ݉JM^L$2'.mkA^麗A˽n-Ʒ!ȇ|I!ӏyD XГkfKYL3zABY3#>n-L>aj\&)΅am{5+;b!FeG#d$fNaIG@Bb=S (,Ѹ)1j? גC'ӽ4)17Sq@CW$i͝;ػ&d|Wϫ.'?Z\D(o?}j2lk͒M@bʯ@ߡj(He&C:(YQ1O4CKt.F 1{ [Sk>SJ 軻Y>1ɕZODXFN3Ӫ1*ܹ.kGmDu7bȞ޾rPLSqT !~*O3VsnAEdꀕ7kJ9j_DjkA+n }ԄS!鰐@ihCpE b1)æh#{mq<{AU8GAgAͯn L(N]qK G9{w5PzBZyKZZ",K(DN'TE tW\Œ:RM;X|Lr KrY4BcJ@ڛ ϑAmqk} >k@`l<\Z4$"rUCRy7^W,Ab- 6U7\_WyVLgzBWlKH_Mc/đ[t#2m3B{Rr=a#ӮU u 'LFDMGne9==A!_N2$Zm^Sgo}\nuauFhJ+=ݍ3_PeTWKzv{BQw­OqZ'>0b曚Xu$>? n||~0~!Y~6*Ҋh"59*h/ROyϐ'ڞv3cմl3"&䵙T-3$3 츷5F?E҇]9?q p_/36W䩩-#lo#N:!r:)+%gƨ'M@eg2v'%Y=Bݤ Xw3O0cI&r/ 7PoĻz8xPCT79) }d%D|*FOǠc #Gt1°3/[$zh4f\ݥ&w!repe#L:-2JA%h>gi2f~v3Ht*ɤ#|cnF54E> \[wkIg2XOϰƝj7a.3i]V R*'NN%tϯh "SsI2\)ƃ\S (stkpp-6W*IB$q?53prԪ(c\@Bm/n*!|C=+o%׌H0߰],cė!_-{\찫 GrRO 턅_YzL _?My e6:!A̝Fèu|Dĕs㛾'@?n]`r4;ֻPJ=qQ8oWn-ɟX+- +eAԲ׃*&;Q7m'Ò yY~ٮPF: K_1r3J;zq? zDJ?L#. XSXq ;V+dçp:X]n|(nCe RJP2X $ ۢ#;^WŁaH21QmP o/C9MLG,nh?*>OhJtGpJÔyqر]VyyiR,% Uy5;Z` ܗHT_JA<`G/.]o@|W2n\ DGްUM_ЖRar;fow^iu+4.Ӊ{qD4 z>uH2dxd,jI=+פjT*GPs\"*<> Kq^j+<9 :8lGpeBXgmrۣ!SR @iy}|y:Ogw)SجXtK`n_m 8hgAfYp(łXnNvM8`OsWVFDKǯ}~N{/c~JwkuK_Dd@mJ,C[SB]>n8 Y[N:,s/xzwcW`V"+|ǼU2#s1WȜyB%w<~ :BIh4}CF<m iÕkH0meViQ%H87%WuzW,j}RJU=`ՐqʚԬ 1 W\C-2~:oV =4κ6z@B KG*^ b| ,^e*̻.3^\HeT nr#}h¿]I$DgI;?O~͗H=M8ʷ s~[~׾z:; .^A.aߓcZ}20` ПO1*-B%WhRqy[IZk?`ߙ<(RhP`j~cW'5IeH ְ v_x{+VWFQ " @-HXg*b#ӕAŞQ[NG9Zpצߔ#p⅌£09JV'oiTK`\P0dbY3Bݳgb E4Ьq` v,O}2_(Ht\K;*WuG"4i6m>K&m4KP4ؔci~{=S2,?2d`DJ)B mqaHr-PYcWx3uʸ2 d2mWk涚yKMK(F @$ *˜eKyQ@r iW5^ξ@[=ݽsoO5#4kթ .-EXpF]:`1!C([5Ҵ%5,k`ZsNEBHm-l;K˨A(}?X> - QK'"kUu]6lvM(!!(O0d)[nLLպS-۟ӱS|Mך"Cz駟~G-L8A\T@0V9QEO篢Qh^iK[Fc(+7|s)иyS0}'y;@E<|&cp2aWI\Fڅ*RO7WUo < e CH&u81RPѯkGl˩Wacom*MiVz&{ Ѕ)ik!9q i5qb a&͓tlE3 SNݐ Z2Ӟ})$>QƎ5 B]=qՓ4 g5XQ_`S.;s b-&3̉DMЕ峬p랞R4`'0s,连.4b6|vZBEp7 LLhM(,r-aGhq>^7(F L+)hV¤X*EB~prRPKltѱ!Y=Dz,hprOkG v>fa9lwUyq/G6rymZ!EǤX88.}%Gɴx-se8,,Xk:K}[ŏ@ wUd7Mn3)M\X8[*A٣D\y&Ο[x;jP8 |! R2 ,tL` >׍ěOT؀AIfwd1*1s2z|kX-Ey:*] ˲k^T{̙ ЖY0Fn0-DЬ? pxi@@1 ʜ5ReM ȁ!yÿIX|1BGb (rqxL5U޺mSPց>9~9oZ mBS-k9|\9Pl dqD+)_J&⍞X6֭y`RLWs1E@zG}}&I媠W'i_jlZz1mg^e ͛ԖקrB|隸mԸaFZJRrٻ㡸 2K*6F\=^>[.1]v~uLeG(WbHm:i1fMiVmWXɋ-sh X &i?cnJ_fJu`s503jyRpj&A X@;LdMWPq32=-B-fɈ\6B8EDp h99X]@+cF.1P@?`a[vZz sY?]F?;s{~悈="](σC\.nj O7AMI+ȇc4B1Fs :9JUɞ>p=6r4_5*4R2iSN 3@7T' lNO7g)5r܄K{EUⲹܥ ~5%%?3rl_zBD,.6dH8/Չx=E 6GnX߃`р0&&_U!T9M8g!°o}\0چ I!g6uSj cڌ5bYJkz)^9G묥CرjRr'ޟaGP%ldA'YBSoBg<er0P&yĔ$y7(i'mt`/Oup#PQG70E+1rO׫!CqC(kڳ51k;I=" G\krx?r1j0s#R@[svx}=Pe`u /ܙWJ!E~KuvdE~Fp''LYQlb"ܲ"-BPA T1fElHdk'UW`zj՚Ç\'͘H>a"c-p%Pnf`3F*Oy?qX'dh"-đE~S/ʌC2a: s{5<[DO;R-n%7%`Ϣ7w2V~7;.X"]AAʉi"z񬘊#Ǹbv"t6Z[ה:Oȼ{J^10)<kqhOmݷ%!_U5&V $ +5Dc?3?qy?ػYqTU`GW5όYNd-x]N]G*S.x^`wq+.g z> m<'.[0UK!#yHᓒV9M싨nNZvS;_b:DD7alۨNJraChS3V 4#?3-.chCt1V$.:|h#tB*%Gjdp@ 0NRj#V73~\q7q-wti:y_\إ%զR|07[m\ 8>D^8@r [ 5Fr;~ŁF5.:\6 M+qbM3%'u yI|דgNN84uAAcH)]IUFX^ aùHCf`13B%: :e` L7L*ceNl-oZ[`Ө3ib΂ Bȏd.'A$iϩT(vsiZ\qv. 8j9K"Pi p]{MQҬ5â197ܺ V/2 4>+1CPQ|$? [Zg/YчP#U @_*x xg g;+=JnfU=}`lrUwC3\ߞ$M t-f`xzS14*|JxKА'Z0uY ҳYKgluA%ND@:ƜtG(}GugV>ьg7`KJڄ6'6v@&a@XȐ#{ e#jG2o(.g).aG&$iK$*aU!'W*z -55&' :U{qr@.![k;J|!,6ڊK$:1[ýV"}eP"I8\>㏁ i[IֻdCN$8&: 8(| U&e8&=i+_SZ&3M@ϓt:F4fwoPh uM<^!Vv,ZT-ң:cV+N 3F0fpCz]No2p#a<̷^Orb*vJp\Ej/(9I3S2҄s'nCc\dFYP~ j4f><])T|׎߼/FJoZTm%'İhѪz7%lh@6=&_Riҏ8䐼b+ɶɨ}2ձwyh5/R,nG>3zi_whZ',M)@BpY'EMpcy HyAK K=:KY餗SC8]wrx?N4<4iQn e].%n &;Z.!<++U"i'ľ#t9+ڢ9= ɻ6 Elx yrJx7+[_C1檳ˮ!K \ІNfPu|5R9 W[ p~dV]+P^Q*w{vЊX/JQ6Q4_kUWFe[Gf: s/8&4=_I=PJRey4>Sx$'-5`[Ϛc>U{"g5{~n Y[P2j6uϨkMcXtJ4kJOFbojd0bG3S> ǩRf*vtU Һ< ?G?(wB(,b%{?CZxGfotXglRf?ЊҙIeT[ƄK'f? y7}>/ v_u7%_u^ U'nkSߔe{i H*>Ggp g6U6|HQDo`GdcR{EOQ1pKɵGrV\ yR(ݩ(DeSQG<5>[(&u U> stream xڴuX>LtǀtHI 5tK#%HIK!-l}/.^y>3CK*a` uCXl%eu{0Udjg dFr@& bP5@}9 UL= #`09@XMM\j bH98z:[[ZA~be鷷$@`6()TܡBk` 28X4A:29uշjlS[9 {& ߒ 5,)&dp9XN_A+) j`O(faspds>M+k- C+J' P6]@dR u!. JwL\@Hce򏯒 MfPC ` 2W rw-]ڙO7ٶ w.gf G,"/+ɪ]<00xJ~^P]R=jI[Cy88{Ŷ; ko]߂\AcY  0b}-CvtpXع|-@To7 [06@Wz\P.pK U UF95wyA*J0s+=p߆&vi_&ڠ288ۛEdf 1CL/A#e]cyK]K3[0/7z*zrwm9[-<ggOT.p6y,v6pt,Q.[/`2F|])Av?E߈EF/]Hf07rCSBWz m@_ 7o@_`[pny~;:jb`<@,2  ۿ C KSd~.uygZ}AsJfe*)`r]ӳA}DmX0FLg~e)}>{TA#Ƙ!˞X{NZ~}+O*kwIySRw͌O&ܘ#x ]cf&FhS\hd)<*JVY3\K1> (Y[ۀ#G*xNF0fh(h5r:FtL8_R%L>wfQ*\B",R7ұE5j9 7.12SxHoPyq~('mcl[~,B(nj;K \O6U+lvk"{Ʀ[P?[ pOK&eJd fe}#Co'N)gTNSzez13@.dy幜k)X5ۓ-'5"exw&7mxm.}V$9r/F{ޕ5wHG"﷕ Ѭy #n=Vd Αߔ0"S8rS/ ͗Ԉ3? ?Hy"kĎخV'нw*}=E,U/}s"&m8n`؂^8#6B ПX/^4OTȊW^[=Օ!N3-={H@MنcT[ qi[AGVNVhM>q2h[gUioa"Fg xI|hqe`zާD#C)Xn~Bnr$arْ$MOq/RPܷet 9z8vY >׬#`:KqgS۶Fܧ`iq?dtG5ѯ"h[E =8]E9p;\`:J7suZ" _(qGnlVXE=, }[l-d8"@S3© ?R]4eg9R`ͻWp/q7,dQA׈es z8A܌j7'Nw9ҒDXc*om?wO3VONA\$>Td(gyWT!+3PU )^Fwi_}#^߼嫒?%*u<HֿX{ԓ?)j­d,u% Q'0q(W LDnk:*|5\mtʤ,8],5(,;}ArR!%HbgCn޼n䙬O"40-M[՞S>#RC j3酮kSa' }wz2wD5o4cގ|C*vbB~\@I[ !ZG;RA RoEx=BVo[\"q qQ!^-}Y!kj'Ry/ =%:]2jg꩸bO|E+5U5a/jݾJn7eDEQߦe3 ~%4f#,QNu=/4͔m'΂,MmR~o1-<}̲EzUO/u"+ֳQg_Ŗ|[ØfUbhߊmzfZCx8FT@L;-lzك Wn,FNuMew=.LFvיA>(Ui%Zxƙl{/jLzծӫ,Y'̈7Œ w+(.Q/ $%b7mtKU2-ekN~ ] QkJNB*_m_0J~Mxe5ֻt ^#Z.J_]ݧvWYev}dO-wv+\7Pg[5n*l|b{w(Ļ[ę(UFZ~Yt T"_̅F>$s%5ɺ/wOb!_dӭ?eI}|، ϑKg壁̹͊n ߉[KnϖiG,\eD`k^p2 y21N3mP_JǍ_>&e/673m}nniB ڥWvT'xWg;'ae5ɘ+~_W>ˣYC41e(N^(=߇12H8ye 3~FMv39:= 9}:E,(5tizeQ,/#x(gowgYip{. Vzw2_ { `4yrv%L.zIap.Xe ^ 84$0vלj5OxB]FLveyq$F2mu4˪ye7.uꆌ"F{.4>4ǹ)8Y^$@DWB |W.N\hZvWg?-exBv,J^Y $2 e 'LGHF&q%k[8ߵqa%\BFϺw*WAbZ |=l@aYt[O{;7V,9+%]HGŏuqCq(;&~w'qSW~a6'ժF7ZG+FR wM`^.gsagH_%Z-*2)=s@uGvGŒEԆ,qjWcwf1] fj­Kޕl#:1hDwhA1HGgs75f}|GhT׎@v+..J(CJL{LWN_5H0  WLkSV+ĥgF.j%/Ոk^,~) ĉO9;xoAdUXbR?%`Oa<||G"xXSfn-@c܉FWfO>=Q}a $s^zK*µԣ|: jʰ0H-rU(,8gEv[(s; ̑N#TFyJ4F^ ᧞;8Ph'6 _yXJ.us3U=je׃ˉ/FH'ݛ>>b zl :,F7\LXW& !Z=)ir "^e^xcueT)㻸^λ 'MCvj${\C&64yw[k?Ipѳkrzu*␋h~\t4}\19m5t~DYpc4ZĤͽVti0W))DnG,/D.w\姺r[Ru~\l0he~-0]rwt)cqAc*F"=X?V?j{F9#^(>UZDr6ԓi}_/_Vђ;S XB/ncݰ 9!i9Ӓvxi9K)1Zcz嚻}l^>}>W1"{vdlqpqJBS @y2KtT+ VL{X~n9~,,9 V j#l)I cPr[꒸@+E 24lc#HB$U/'Sݻkb0 m}^kҁk-ŠP'h؞Y?;W!现B6FEɢxi ";O6%Q'/iT[Jl|wӖG #Z~Fm]z;eEApx8=%{%_@b{Teh1dƂ2'7&ry)>ueA8QidǾ ^ dQ~85AxL8-_>MTzBvA@y2MoO(v"}UEkh7Be+| ] J/* UCrY1ZNJhge˂eFp}OED6JOX>,>{3\F])v;z}Rz&m\U)m(٫o,i%y? UqAmU}οi*ݗ8i|68DM=ͤz]W/\$RZqx-jψo Ļ;}HC[%N1PND5Mk1r_&Q會h42C%lF<#IiWza#=7҄QvxM~zf3Tؙĥ<6>֧:$ɑ K+4ϷUf0TH2]CcHfgtfKX~snצY\?mavnQ {bwdWldem)aD]?$fݡ`l.6gSlS!˕;7|& N̗E "%EY-#ʫ )@ptP{3¡vGdťMrR 9 8Ĥ?v^30. ھVq{s"o)H[E(K',1k|^vپc=Zk 8Oyxw|:3AD vlx>p03t.>($.j*|SK$+E%휪&(Z̶9;Y1v$ [!~*6Wi4*&!rc׷͟=ID098a @0'qe(᎛I~횓Ԝ5HC\_$9n 9ST//F7_#ɯEXl+e5Gp9 Q1A}vym%;P)k_. 5EܿȽ5AQOunLXߜgg'J3;>z`G徎,&^m(yzOEX{_?A?A3z!P'&A.X%O)IϮ(˩=f~ ^>II Z5z^0z3957P1(o" gKp?JK^Yf44 KW7,ǪZ`ޓ$ӖWJSmv*nBY2<#'ECiBXѡ@\1إڀZP&>b_zO9(k4TڗSKqύo970$%/|J %(3NvfzZ4P~:F/ltL$FRGfK;1n.#sw,![/sӮ=pBn=ƛ$|;*.IngX ?RmӀi1c2*zߓ>/^j٬_hQ Ђ=kB^U"Ԅ!NU%': Zi#e68ƒ7AgwTQF4UKDBZ${*W8!S;JK~aI~R;NO.{>4٢FHo*=qmLD];3mJ]d8c1J7] +aRl&6baYr(!ĵS,|m:}h!YW'gč vwɍ7xmz#f|W4{p峽]|0ctHbi>ޖ v+ xRά-ݜS;O2s1.1ETc;~_;JPjV3 Ùzm$ N%zWqMø ֝%&a|-/w)zU2oޢvM-[mM򿓙V|Qfb0]7]xZʉ& J32,>#=~~mhۦ0 q?XAwNr6̚b:L۝F֧!ojodh|t1s鼓? ".9ZD_{^@*=U.~ӈ>\ }X,z@:n) 8~ L>|1 $y]vx4j!Sƽ7޴!cq{-e8 W$|LrLV崵Ҹ DNԨMOn^sIK܃ LJ^˅Tf"L7`y!J?hVD:,w˪,nnWO. *ά[6JYѱmMNVbg?AtD&џ7SsXFU2dܠ@ca+MU]2%O7ڮ *lz%*O /hL~6ƕk{FynA+.!/.F#H0 d Ke_.eu]?4$F?6۬똚 Kw4 \8z9c8~%TCz.Mˀ7;JSR7qWaJ-6 *<&+Gыi r/P k^bOt~nR3PpP0;N#|zX#;tާWw EzEOOM_j$>~RV.KTx gn4͡7Ӈo0jG~((Q$ Q$00-he%"#B^ONYv,o\9Qڽ/ *ig %L D)}aVg1gy@xYdmC%a#}ѡBYo7U"<1#VFL acP$ǚn~ ZH Dڹ ]_8}kzeGFcGIr{q#*TorqxłJ4|V{H70PΙlxu$!`I;wWxƤn[ms Q3KT&8B]Lάc1Q8ZՕ9*.g2Ẵz+:-4B-1 7y3~Q3EeCp-= ;uK6ԂcnrվbwNjDŽ؛mV]4Fh˞.OOlq KR}V:tAؚ>{փ ^q+J)֝oCڥ€N#^})1-^8X=cYg/ Se [xTM}\m1!D;+3؃I%@{VI XZGXGumM$/܋rz*6$`<ݍap5rrQ}tnߡ#o0nyl1A6>Qfn9ϥb'Xdm(TH Sj# |Q3otލc8Q5MD8PzmWjoE Sndܻ;)W+7rkT 38SVln&F,Xwv=6; $lÛ{ENW$s}$CsI&1D(a&\~#+_!8XPx}^:0ZpP[1ts$`Gr mGs\L#$+r,=RJq1Ҿ:"uJDXK\]8Y#*럤c+ 􈃣)Z9gUzV{BE?yr^|Iȣι:$j (4ll;(K²fܱJ Kӑjjԃ2ԮGkݽ|)1eNBC>ym!U Wr[)n^jh݊XEiqtsޜO7KGdȮf>Xu>6ْubt&S~-hvC jB}@&g1d#;Ёe9gH3Q92ʜlFɢ>>n@F^ZtW1]dV$@ Vv.I:l"̧~Udg?H=/IDOn(LTuRkB'V~r^13({FF G1ȺhNOѾ*'i=a7 S3!0N#}S=8Jw>tALL jt5WJ)O_"}xC1njn(G/;VQOLm2 ,W⒂`0FW'&"aW?hxȵ"Bļ;giR1{S_SὫ)70pvp D?c͋7x>1Bk@2XER6Qho r5]^!})O.TXܖ m}q!7GЏ9A tAXمccqG>H)G;Q%T[Rק+\#5 -{  ]O#ց*<=cE'a ҳwű2!U1x0>-⩇q5h+c=$;0wX*١QIW4Lv[Q|bOq,!퓳trJ6~R GB{rZgggwɘ\)L+3_챢E'4#5}I>GRp$>F@?Wj8M,q2Ei;;#QZv( sr1+R vF*iO:<筟 QK 3 S"0J/}0!I!VF&_2IY' &5qix(~gZ=+tοS:,:s}t2(KCEj+'Cxͭ܃qA>;]{`mYkqs̋WrQiϧasAO"VTqs U߅Niـ0qV4ۑTV)&L6.\1ĐlȆ)Z؞ݝ1AlK6c!3 ҃2Y>Dũ<_NuiI#0sH"lW)`R$@AY 0(.6O/4R.wYO).?PW} xE&c%mF!vWX%|9PVtj3P]z޼qn3F/ (,O1=wg:rBbJT1_P?DDȿa&,sI+x)<m>yψ)`XؗZ a֔&4XiiIh(dne6K!-y`yh(2V8#a%,i ?r$nre`ffBۉ<>H`v+*[WZ%Kg:riu&6 k~'kt8Jl~=g ~LPaNR&Ȝ}TE^Q_t#!bo%,ҍL#՛_g dNGvqIX!-T.E^dUeJւwP|"j{Jp%)p9īk/"luQMз{F{+ =sq~9V_ .(9ۦy40ז]M5^3S9DlsQ&VMu%h] EЪlVI3$sjşO^; \`YL+;?&׳]f],}OyCZR#>PB&E ǽ,u-0yjߜ+"rXFL.yxGo} #z\=i wN h2Bz7|9WWؓ[ H('`&S*|ت;%N0ʘ^~7w %ұmйАjԿmas(7U͑FH8x*5'l[8hi@b}`'Otydrk) ׬yeMf|H8˰R> LMW}!%)n۟Weɮڊ5ooVܐȘE%IK+s6}*UWDFFh7UcU`FllbhbKa53-Z/gW@3)& ʀ_dѭY˯O BCm:I喌IMj%uN<]#FG%WD ʴh΅ԫ0l ,T>6򱧕1ѩFQͲN|_%16o_ C?VA+lFudGmiLvvZ/sd`Kʈ2{ ƛyo#iiBWQfݱ2RjjU,U ig#_Hl_f7`ɰ։̏YWv 0&z?hXD>l۶98]ӡ6Q_XU!lvD7DmJԉ"]9eU0\4]Coci}ڊia o7^~-Zjj DZ_>G_2c[`t.:S sԝ95Ls05%xUv;6keczAɩGVO\n>D p~h/ie[?Z^+fRN,_+Rqi* 8GLμ 8I]h ~_/-,Q^wG% >kux19(;@JoI<뼃z)< s4[3G*$TZ&ы}tYb5{@#Lfx0XH|PCV )`Bc7sA #pj]Jå/WM# N髖!Ym[I䍿@>nH|6̃+h.~Fp]x{D֝E}x7*ȘLi;˦E~جYU֦5˵"P2AΈ‷"I50_OV%}ab WU֔Qfnr8;( `#}}R+_t sAVq MKϼ)u.!E3_ϕMčhJ u/rlCv=&FѾ)4)дs7 "gVI{B>ԠZ` \>ޏB'PZbmF;< bta$Cx*I;SYCgy[7Рy=73 F0*V|&GƢŗ|!c4* \g7Ւ5HYJhk& RV_;'+lQ=wi]Y3{M;FKuIbu.Q5<<(,*B.=AFU.|ke0p4G ^;d-x\!)f5q'_~am<.QmDXj_ix{{T~,}o'/ɋXV-1RhV{Q",~[]TU) 6;NYȂy'~qX1&^^W7V'R6SlBƀ]ܝ^g |-kDMÀ}yi}XDDq5srאKWB͇1_Y pgjh튧'&0)t[~Չj!KŗVh#<$K0zQ[ЙMFT vAC QZ(}u lk";Dw;)v7bͨ8V2eP >5VW}KGqʫ|Ъ67 g]c.Bq;ng?׶]\(^ OΕ v/[nAS@4}'yyś/) bMGSGz*|I81޲<"iJ˭K[rї~s'2Wmczq8w~9WI\UsFUI1FV;ATQn8 IzFedVSl+JKu8zT'lbќ!Lױ`Ym?<Լ%X9p{ͩ55LByguNS&bud^ъEg4pC@qXڐ*6'--!aT 5edRbL?`<<+pDO)iNy$.4~ *`nP%/yTLSx6~fDj4#Fn THs:ftK.t֧_KH^,t9d[+ty.[1Y0ʀu2*Ȓ5C,2ɪ2e}3¹­1G|$20K5QnȨ @2n e3\!0bߕ( >&):SD} CƠ^n,sqB;~Jiț5-,8Hb\(AŌv&bD٥iuiJ2ynβl{e5D.P^kы5#Bv.;ۢJ;N@9TyWzӝYN9fƋxT Zօ&{@%נV#&<,pxiޘ?էܪhܙ2Q~eo:^> pd7z/>*Fy?Rͣ/m qӃeYoZv-xF]e_k&]/[xТ~Uu({\B?ZEIe>A"y)Aws&E7yJC2}KJJ;,tBG:X@2Ϫ~;m0ԾΉVٳ آ?}qލϘ6kElQFn Mq|E9s-&PF ]rArqX8p3a?2;DWBKaM2~EOs8 XBn=X |n(25u#NfK;wH0m쭵K!q2_l7RMW-A3G^pHZAEnDF0lї~K)ʺM67yGA/)]մgjJQ@M[*!eOгxYwpRxsxnPkZA8T:XJ+9꫸]B?@>PKU¹M YqFV܀;iŻ5AVa3 ʁ6Է]AGUOyU2uCjv= [d\ FN "*mk4g8̉ZD|l@SƩКz>c353f?:Ha4/jU&LH^x%)'y١QaŃ,,&Cj6FU:(jp"K=H?}a*!G]j2l^ 1ESZ*< hZDL=4`z<\2<ba|pߙxLzWP}K >n2Kd鄙g.c)_Nq+ۼK+t4m0 {$%@V0k7%HBh5>i +m=rHSret`d7~ (V[l\'l%,&]K-TrE "hDֈD?KA9bjNĿx^Io(}$=#ttCc ᾝvJX"s5ECpOkOT*i3 âLTmo.!fz-/T^i>. 5ėw11dOG/,E$~'ǮW0-VEH,c a+l^q]2}r Gm;/ P0ĭ0Rq٘ +Վ>I΢r|nJi}O|ߥFEAg0KU1E-23, y<ن`>:? vi6^-Z!*f&\/ёv2Sa6f$qE4F?%(hJ292,Z) ,C}qt|sSmueYJ݅/BVlؚNv4>'_C#t_56<럺2r'[%旽: v\/<> c;fT࡛teB 6Q=_CFp>IF]"2I7nabڿcvj",eCq)We(xtQ\;mv/2[]*ެ6B$eR y)&1 rG/$NK>B8KMe3Wv}ɇSuj2V ڽeȕj݆th8^HMcqb"EaA/M̘  2\2452clJ`N br,YjC8'>#?O}/ut s~‚RJp˕mm\6SI~% οӁv&=L&2F)z}"H\ځGKlD7ּYkc`g9LM}_Q/Yñn<);"j݇ܜrfah Lݔ_׺@Py|0 1 Mg :,"{@=֝n/O#6xB;"͌4&zl9U1r~ibYL؆~&WV%ԄF,mi)6v.B) d) Yzn 5V“` Cs/-~5 eI8)΂vU;, #x'dMo{wҿnq8oa뱟utKTM>K2Mt+Nև0FN 0+jI&TS7]{F^3]HVb{I!8TqjajsBycc&۹O4)ߨDƲ]jD]v5#+Ɏ#ؿCFLIEgFN~#*(V&nNS,=AmBnh sz_tǹ%#|5a!@fT7Sŵ@9Xo 2KL՚ї/ς{&Hذ\6)}Ws].dC֬Bme&@*3(ej=s9uYI(íDQgҠ=p%"'cq'+2y޺ŦkPGvk2qW!&s^rDq4*d Lw|ӰU")Ml׃J=@juy`t!ϹI@uDSw rS,v5t}ڰ,YL~@3YGq/;}߃Զ5;Di)kyQ A;[eWhJl`FD:ʌ QQpȉn M5$ẅoYIy ~K k12cIZӪ+6Êj lfTn̡Ŝ%;@I>Yj"e(B Y{4NՊ+^bhrm:dïi;37[Ul ZJd~j] Vs0W Gr2g_.Q*B.B޹ysWSdkaHo ȕU ܹN61vwUIl=!o=XkyT&"BLܧ [է\alJ暓tr[Y$Тsn1V!FlP}ffMڣKdt:6Jl&9!+q1К*O,Lt,E/yS6@tZ .Q*TK48p7ohzf`4D&%rdg%~(cWKv=T!rړ}"" L"PDinQ=-xUb{;/ /kTE'Ü-!\XIip/>{V &@+(ler $JC[ t/_eBpD[%?<"'[T\S*ݬHl5I{8O_ O1PZӌ$| 7;mK)<^.564GBJ%eZnl^#yԴ-mThpT {:q{ [Aq7"<| 01G׍OSu)gkl{>4#^P~NLI=,*q@.4}$ 991Nc96Us"ХOy1Rxޞ;Dԝ/)r)euzw ް D/$3e TJ&脜<Y:63|v7C|V08cۄhʣғirpEsc5|,Y*h>t i*?Y/ā7_/&I}@]+ctx]JiYժ`_Cv?h9Ѣp~mcS2gxꄰc4`p2j-#J+ԖhlUywNPGt1\2B0hp|9>R#PLԈWg $"f|86~Zb.~iRʊ@@QVfz W)&gX `EWu;U{ %:?Y2qb{u[N~;Kx<}JKnЙCFLm0pb;z܅>pOZYY痼OȢvtci7^},3{>ftYw3 7Qk~۲7tzn|= ׽Y) C ÒǠyդi?(s3Ա7J+p@!IC{N@B^88lR(qZ5Lj|v7LB@c3{CVw߼ȯ{o~b:+\ cB6mT"Rl.,~0%s2 e\A$'VB8&f2Fuwֶ6ȦtמN= j*Yի${TL3O %zTLF;P`E ?]SSoBI&a}-ߵEQ~HZ x_:Q h{~Ķ4D!%U"'H Jh9rճqTRX@$Z [ǘtdJ#fj_foYL k{4P78keUeQj87΍B,p%S':DmM }`\!+` 4DyZ m_kpQCP{:Ifvn'{TmjYvB)+04zEA8ĢC9kډr:#b)nZK}bbS1=MV m伀](\HwZ|wk Ő lE)7AK_}@Mlnu}On:S_![YٕPGͱ?&Ӫ[j\UI|xs\ʢD@ t\b4`}aC20C^KPGiWgh3a ̖Q՜n6}Zh1u852!v|S%u;/ZS+Ȣ(Y% ;Maf@cZѴo?~@Gg{^LJBK"Τc*Bf:EVwvlA& xyM(U).q m'CSy@Gۈrw>_0ܜDܓb1W=R?f ,y#|{?>ўGzs#B:+BX-~lU[K> S +z'֣KgrT]["by(_^:!;ETԨdG.+oAU x¸6>d~tCOM~J<烅 Nb8[mfAiC1D'1T9ϡ| 백h]tEMBvq f >6x.kQ6ĭ{ AË7Hg(Lb^ri1}YBT?_Ly}Q##bͽp],w+2y޺ŦkPGv-> $.v <6ׯP,ёV!h5R:z&/];= B3*Lvu5EXslDW?-0Б=_ߚO*67re) 7R$*5RV.VYEZpY"F=E N.I0r8uCtԛ<F!?Z8'!-Ѽ& iv|JwS`}Dk?.5|M_9T?DoN@=kxd"g-tp:WAv,dž(׮$W1~L\_Ve$.(@~vE]s%a" )2I@MUtAwCMh-lWz,0[NGDߋ8Sn?q T _P}i^`erDܜŐn Hn6sƯq22*wPkhfp4.Q.L]Tj2S_u E݊]Ymv_\x>Zj\l3nTg<=h J+uL \Q!dǒ,+c/(2[<L`MCv hO pF|ڑش Cdfb(n_Z;}|]a]^Ҋ_#{+sF &HϽ@QzbnfՃ6ѫC2d%'tOb1k&O^gm-3j aI ՖhsG/,Xڶ3OH⚞vPᛒn cΙ>]xܺllmGyf|W:^mݍ$]YBu)Nߦ^>B]ܑG5%jI%F @")^TD^/K~5N& H NbMc=\eX  s*"` {0ߘ@̽QpejKC΢JEw_f˕ D[-`IF_N$5rgSB+Q+)ڕ$e&T0v}+w^%_QMtSv@uB^Y?s7+G+ڄj[;,W!k-%/,Ö-0++s-|5n-䏪?dmWz}XPM"[Yŧ/Uy8q>/O%6yƶ(; <2ĤB;=-4SB,Dmüql%/i-C(=$[J϶k(j76us bUV VnVF;%cgcޱ4P5#+.CޡaoIY&_Jlܶ@54cR*~3.;ղ =9 ź-+rR4*>OFQ6b%`3ǭ%PܟYˁOT?֯%ۚ+Zk&'ݴ=̞N_G ;b$t,{8yf?<]"L-͆D8By_F:*; $Wpτ p 鶹#>?~Wxօ|y(=pPiǶR_a[u#hyo( Jϵmj.D"-11$1ǿͳ S?˄p4;,{iEU$ N'&a|cb$xbf Cd3\ sG$ k9|&^ݍxWɐӜ\Ak:rHU{n0_ P'kj;{ Ko#%8nNhKzPq*IPepcHPk1x _mBbTyIgH:hKrI0 V\W.tD'SXG{ W~6T:GpJ2(P@I A8؆!4++ xl \V+査eFH) ɸ;YaN} L|Î ږK_%ėL'16 % s>:4yUMu?""P@y843Ư߄Ŵ˶zBt%QM* >(, h1, m}UL;pĥL))j8J"Mog| s'ű_lR8<BS HV%B``st\l| Vͤa݂Cj3*WH~1=Jd{@nd ؠ^lbF' W GݸjuFS 8C>xB1JUȎ ."uȟX5,`VZҐ=U18WO/Ӏ,C, =h^.,Nj?/y&':WO2fD8eT3~b*tN6p {鳧 ,GsC)ᝄnNªrZGG)5fk⎚ӼL!]z^A& Yy ܳ-u)C?WB[U{㥡EQRGlF]t2KRI514E^׍0[{g߈\vQT~jr<ѷx"|DPծ(Lł~uU@a@@֢e˦;Pif򚲖oE-8L^EjɌͰ Oސèh֚(t;ZL$H^9̘f/M>ٱj #NcwMzI)ꁞwM<z˺݇#굥L²ǧ=v%zO\)FKe48Sダ&K@r'> f/%}ce# *zpB83syb:K_vzn% -Gi*IV*%zLP @GԆs?s8*ps endstream endobj 111 0 obj << /Length1 1973 /Length2 21896 /Length3 0 /Length 23146 /Filter /FlateDecode >> stream xڴysxݷvlۙضm;i ۶m'ƶmAӸI4֗kg3 QVc17J۹002TmXXT6NVFffv8 1' b P2u`f恣HNF3'@bdP%(;0;vVv@1{O'+ K?9d-56wwۙdJ+hilc7jj)U% e5ƏjNELM]C ..j4>[?|8 WPQQ`a la@Ps'{ۿ -]\x-\],l§ni pw|\6jgNK @h $iAzh˟6(ciW<hglgb 0KQ surSC&tQx۹:{[os٦vV.g̭l;3+t "2j ijcP_9,<Jؙ~v>q>;y2ob[ٻys+;3?7su`Ұrtʈ _: t=L-/,vw8}́8ogc7 XfV.T8.pe3@JqNl<f@s8&E{JP9iUKFHzߎƶV6_.Z?hlmf,i4Sr1e\?/bgaؖTw?՟``/-MvLF `RVUQߴO 0vr2c+f@`bw8l( Io$/OS3ӿ$.?%8c[hY?[Sd<,v7#Ϳ<&,- `rC:Y<?Et.NnovW^@__{?s/Yeeq7c'+]Cx|hQQ{ovVV+%>&G|# ??zMWMB>UIUBR0Wc j&BgudS~K +զy֞R3LE>d&FPZ@e)͉l~NBvGb@cT1u:Tcҽdc m#>x@dvѨ( kRʡM(i ]'rNδg}X\=k|O)1I3< "ÍML'x6{,Nጴbi%Z eLVi|8b+dHCh +3ȱCFLtA'+VSL>h)FR_2NBP\xB m30.,DAj dk.7RQ`' aPdKl%A{&Y(dX̯B(N]Mq˘ꮤԍ1_ąlM zV4b+چ7dhV;f{z'3L@*grIEBVr51k rsN0E#e@tEi+`Rd_m'!b7iv7l.'еd(lpxS^|,skBu,"SNL'V}5*ɰSh?EG4:$dP.Ou09|eF'=ek]Ł*II-a&zU*'+&^Jr}YEŗŋYVwYOJh PrҔɫ_--= ڳhdY>(Ta~ YOsd^&gH/'&6({_y&!&mAjdN7rF<Xq!s唹s 8BiX𭅅UZ:be?+Å\^9gs4@B{$Kp<a; |{&zRrU=>4y\aS%#xdxOC}q[/ [BɫX-Q #:+3R ]3\O&Ux EWpZ1/YMMK"سd6Qh| O` *Su1gr X'<>I *.5Y!Ԧx$R.']m@HqrmyS,&웷~G\7] q>@cg'(fKuXWrHZkD utmkh_qꗊU/z5Լ.~[ ^4$3,n[''0ۓUt\2vġ"E ~YdzHCtTj`G*jXMq33[v,(73,8^\ӱͭ]]əAXa( uNX@%h il:k(Ӝ JBs+;pIM $p0.CdߖOPϩIP_-{GK"\ớElC~q {A&pa26UE9}!Uvgz*vZl ʠ &X%# -=4IiDù>mznc#"GMJH֟&p-W.f3:|z16Q 2;AgcOth9!vKVwkAyRi$:CIs~|ᢰ9D޾ñ`|lU2p 5:{0]E} @OJW&RU0;y3:Ryʮkf:'V('u* NY@Q*N RsEV Vɰ >bab\JYs8Tykp|  W}gpKvvyl2LQޗ83Y=~457r ۘX[娠s D-c>p➷VR6I)%6O/{ꐅT%,f E\Ax%-s0]uy&|Ig$ "fVFrf.y+HlJds@[ƵƵc6Pk䦨t\ZdK\هwl3!uOTRVߌEO`$G~== N0r&ͅLs)lSEW\2wpf6 +(6[7۝[X 4F{baY;'f~n_恸Pf.dJT@sU.΁A`gnV"bR!s<)`Tڜt$O͸3O\\*]EYŽ>{i* G 6c P}NǶNU8?I@X8eH /Mr51-#UB a@?ڍ$@ bufJ$."R΃=gBP~)G>} Gy+R̯Uv? 6 %Yu}~biTCvŪ Ŭn .襽EҐԯ>U]%S~* Yb SO69#;LV4]BWžF$7+KȢV(⋓ʰ. Ձg  "7xNPdÛVq1{UJa S5,e Y zjD3]"h2. kпȗTXCL_TI437(dH9dO9m8-CJ6̝A\f^4:XԦ.ݳM@H܈!BE/뵝!oY_{BQrM=(ge`HMP1 %]$"=7rɷp=jC;WبbFϡR$5%p^Eqؒ$ Eh8PPMvDVz)aVB\]m'ʾ%uui֬dH= if0VAM4*F݅-I06aܞXVݕN8U.|,h'SWZnO厐fI\f_&]Z!ad* S_"P?Ym/LֵHPEMewJ{0ҙ*g%R4^CZ~/y?]#U¥-wqUِWz{bv3 j,p7s/B]؀`?|X. ^ ^`M-x d(=qze{#A$UE : lkZ> (7mz\@y^aeW2S~z(@Zr׮DK2VC&I}rS_c'.71mږ[:bsXSkJ:m5 LIYЁc^uAq7{8ݸEfG䨼]yCsHi>=b,~>җW돸?%3߫$ęD C2Xɷ.ׁ`ix{Y-z V*6c˂c%*QF41.(hbJ.sh]q$T^Sgdc\$ջ]#ÂeU=l 2\|,):-h8Hno8hG;!'#iPjb+Olz9Rd'_.t5 ϐdl3Ζ-'H(lf{OM6TbU5N2]r d( Hd~/?iSzHGh=wظm +qE!"4;ƇM4|[{I^ڋ#XrS P#f(*[f5*S[gRR :ZDh*?ۡ~&\GER;~9 wBˤkAf '9Ygw?ca|z#*L맸񨜦yq>(HU꽶*fHUJ)9r uGp_΄rW^ .{,}Pycb6Ls3N!1CppMob:_w|(eB==*laqbĩ ް6Tc64ɽcv=BvYN{̖5d)SkIQ'gEt<_U,{ wbّ~2`xf ߍn3vguY>{Evw0_I*Wëze 6zK zKe1 5(sDR++3Gxm 'RtO_pTDHnE,ieґ Ӗ;}C\ ,mksK#]$aUZj籾ۢ+ AoSF9 &M'BJ=}y$[1 Evf'휣N4Cۥ`|dytc_d9!*Ē}]6۲2Yj*X|Nr-q*PoU[Ilw^.X[bbX(PnXKque(SàfrpQ,RލFwH\)aV[#݂`XDQ [?&Ďg֬A DŽ27u*2;{+.] ϫ%$k:i9O?End)<7тqhtaW|x̒к+O,į1k+Pdυ\TĀv· Y[X-|u(wycsa0G]+h'+5:2Ҟ5v;1H2ڔQn =ZeV[4zڎp9SNo5խ^E WR0;E5l(o PREԍ)4T=vR@qߍ@xs&wmnUxʤ/ķAR1qk|\xe?ř0B|slf4pn oC _6p5C7IS1cnQ LNfqee`':ö:wpW$b"1D8㪍|GJEá ->(e&k冐+ӟa:ʓ %/n`N2YdZWEy-K]5"jX i!yY [Rjx_[,$JNv<`֌*5MwdW/@M tBoS>,!t8Sg*"pyG I 1?m «!-/58Q.aF'8uJtNwBV2tnEK_=f5HMRt뼛t^9[ #o`~jTsX5̶?ζE@~Ulwc_MDH^ ]WBNl-=[Omjbzʉ~=^ 0pa2geUDy y>؍A;YE#`q ťo/{rxH{=sk[/Ưj\m>7ɑÖ ?N#*AI}Q߅neiMx&iS!A h*&6AC.ɓ ߟƅ2J.;2I+b+Y)pI_& E;) ^̥ˌUtk0*HIAY7׺5)x6 404Ϡ;ܖRP[cpfg>[u/ޖJ(ch܄7kc"X '\$݃4n$+g5LI8Xv"Sw4 yZcA8v~ΜmR+;Gp@b;1;Fdgä;7k+ʦכ*< \K)~X-"1BLCF2rI8 t?6Gכ"1 ͕Rm徝_x/"Ke+-sɃ  S)8x4O댈1KoQzaqINH;n:)Qa7md3ԧڠ!s!rIO/f$S@V&.ȒHyq--˻1 "?!DH[ːj䀢 ZZR7>Lֿh?d3a09#0;] ނW@>V޹H<])"<m)'65ϭQ^<ŷc_4,44~W4b dd*Mv]SsM!c\WceBZ_W~&l[9n0L4(cPX=n݆:ekˋ ж1s`6GJԄ]Z5o%|<33f8n٨#zIdP:H&ktrJNs:aԺ5gQ)҃S@ԺzI "nəpo{GW&HQZ"d#GRZh̾_NeWEëS tYr<lqaLޡk +؛[J꾘Z'e93t#}#lI?95do|\gnDx;4:O€$!>7kV;׏y h@@4ZTL϶oU|nfd$Sق0h6٨}QQ"B+\ygi^3PC>17;WT7FӲznrD䐳@޺=愤LMgb␠ Nv Fު&}s.EKRgc6(I_h@b8#=`I"Z8iYBb}ʶ\_PʜkgG4(3ڴS5}(I b  )צR*)bmP/^9{q˻ VO 1o\u' GAc'{ycZ6!0q`@<|{1!/+XBP#=(?F8CSC 7/yk oY~wn$ _m\s8 @}q!Ce,8&Y,ٴ7:᲌6AiSyxLP!(x7Q 'DG!X/*zW&,O֗$M|6"Φ#yۜa-Cc[NۋqjHMD%h4\o0ap_!|2gL_{ r$QZHHWjb)\ؗ;CR'[ [[ ])H *sޯ:n8<'[.ſ?DK{E+ޱJ%K+ !xr/0*;]h'28 W>3RmTɽvb6笴譮"KqSpSB;^"0J{kc4pi ,K!؟^ mMWꦏ.#NaэO$ZL'JV[Zh' ˴̕#<ǫIHAøO^A}zq\[)|jlI!Êkd$,/Q&jR\sPܴu1 cݔX}m(Rz`ʟc1Lc]솹 >#fp)pBdO7^uQ n7t^2g1 Q$u͛Gj᧓A9ս-KN UToSբe w';~d+ <*qd/vļ8'kIjG A!/1bB&1]_FYbV$m]e .DMxn5R4\ذ b`9' H@\89\dxЫ M33vFÖx}bշzn?h -uHPs ro1DwvKzۄ|ֲi3 Lh$Ruhg};1P;.]9 axf #mQ4妻A?l&5J6*|c(GrR rkȽIPEE_15BRC2nk"0z礤&a2GVT0Tg-ov ' ^]} Йڼ˽ӳPhw븁IrE ۴“tO6:aeIp_%lA,nn8m9L sP`ooQ{1bo@N(gٴ^.KƒU[*ƒC{E<Ȯ?$s a6gxnoIx#^k3~'(p~SP.5S-X}aE$urBl/]!9o:]l,4d~ɇ3 V[h{=nu>j(6=wB_ak$-uL 4sDhV~4!S$hV$~JXA45; 00Ib4GV1yرZ ήxDlsS|SĊS\ZN/-?Nv%+Gyvx{<+s(62 .=k2k.ѯ}}]^r̠#U,ɽ6# zkr|胎 H٤T*hѩicu-tկnh"eч*ub=7@K^&zSeI قIka6B'\YVs+!Lb-w|ɔnZ2EN "$F#;ap ` H(ajCl0HkdZR6*? "{*:xAvtݭnHLȥgs@;NG=̴v}HiC'$5 7k5$9++s3[!  2&PΛY".P0d<8ӒŲAI~tK%/?= "kP ha?AK&l"=Rl.E{TӻzCR|yZ{&q1 M)'`ed4ol{~?;kL^XWZ vpX;%N#5z #!Jj w<[Xgcٱ?[*gu( ,qiTC"-i}J[A9Sk(JCG"6R¾dm CD`?SZTu =yӽ-7Up'ҊTGyoR= y߲-# H_jprDmJ5kHٲ& Z`"Bx]Ć7K:j~bu@>CM3Α͉ݪ mEeOeTN* ux|&Z9d$erE<_CQ- Ugևʠu~Srw<L㽢@ S䯀O_ޛD#eT46ϲğ=}~br8!Wz5t{ B}jR ]SJΠ3Z|]:ⳙn;9o L&"} ]kMEpu];~du_gЅU-o<;-JO$)N~y RNdӏ, iEEԆ3;F'FbζTwPi(5CG}3i!'KMEN*)νtzǜ{,`2u ?!JG3a4);ZDEXsTot6JB{.xop%P2^N}iᐨz汹y2A/S-o]Z CQӖ%N4d"V+CZ\r&[+Z8'!nk4yഘޜ_Gk^B4Y"pdꐑniq'<"!~VOokc8m9EZHd+p9 JJu(o{U˙>B=)POE9H"iF{ 7xD)~1ߡv'niVvk@`MsW6Ȼprf J!{?nC!ND,>(q."3!j HTFvSb?%D80lrj|jH)q8}+{"PϻVi/sV۩5Ld֢\Ib֚LqaJ:ԙV9H4ǹY:$ʹ@2ܥmŷu!HMr$ nD04IEa.KKYD ZC3vca7jo%<ԊU~8M(w/VOď>s !ĚxǨ[Tn$-,uV3Gm7( }Åj"Vg f&S-:> vbI`1}*,CH)`@Awwto^*WRkh$ z3U|-+#Kj1Rrk8f aë"?>D}&-k| >+<8^=cZz4,,3]0R " X#EiwDR&J:!Wxև&GQT& @+ 'ƨ'rQjN='h RbnÎo[ h!;+Ǜ7y\[ٜM+\W c|uRSijU QGt ݻ5Q P:2x7CQ:m<#f(1 Ilf4G(+*?kcߪK5z#L۲8rT-T ,P/`ti`2\Bq|`Zx0ϾH&^O ;\cQ|YL<—ǥcy+ 4Sa J/?4wu՞ 0Fej b^̏I˞cܮeUS}}9bʎǟ<`{ࡧD ۍ2' a;T܇'Fѭ\$hQ[N\jD/;3#~u&I+|L[ɻ2 tbLC< ~3b$SVE"֔X3ڢf^MnݠYɬO .!sN@CppOSOm6~KF.Apކv%`GAFA˾Ͽ60*ݙMj0 neGKux\&/LZ(NHs'-gqf|Crt 5x[ BEΌfF=C]^za>r< =7Rh 5Eݡ1QqhMbopDav\r>:Gl+{^,)+;'u-c7W]a!Hΰ$J؜#fl[Ig0h+Tܚj?ѼdILgA,ڭ,3Ä1Y?JPiykJ;  #C5P,(&Wgw7Lo<ޖ86 ,.7{On?YSynZjn%~?ǬEcRH emQՁ ׷79QLI20i̜Ym-;…]4H@" athE76+b/]T#J󁕮qwa72I;'CḐ3J(#}^Jwt:^4MD2't<zj(aӨ-F6P]m7 @m"P^޸Sss9=_qa@wYS_Ry^)r3n`F}E#A ll| %i3)yliq!-%;D Έɾ}Jkc c$ 4+T>Ϲ?iŷ2!'U2!1-C<2gdk ~l&]ʘT\q gc-$&Լ9o7׳Iin Q4"e ƣEeGqJ窙yV *8#:_wvv_n;U(О^zZڑlک(kxIZXtbX #xJ8;QKakUu%vHEDҏ<*RufIh| \3Д`\"募>m/p%@q9ng$/ Fpk{G:-naIk+9PI~%F4j/:֏39^sݬ燌aB_2 _,әЗ\ 4bbC9ħ_/F? A]bu\k)Rb"ZHii2+c 'd .ѻKA0U%Kv}m!)ͷx4&+7ꥌ]{$+-2d`&7NUS@BƩJzKp `-Z 6'i[ߚHs'I29AR| _+u4E%[ϻ#+b5|V -Sk; 9ǩH::/MGiMr{j 8BNnXu>3My ۟mi{O+!OAs *AڿئǷџt #n;vfb<*')đA@ّdVn[Qy#' ՟шiHY)кU6p| .`y~zҮ=<;zD~cl$B}WJ%8FsG$\r +4F]Oޝ7mrGzLfy)2?,%z{7}F5;R_UOmM 8Mژ$3u`OS']KTۏPڪ;dQVz\,nѠ0uHgG~JM~ JHP".'uw|HK$D(9=Aه¬Tuf*Ȓ HVQE endstream endobj 113 0 obj << /Length1 1925 /Length2 23122 /Length3 0 /Length 24293 /Filter /FlateDecode >> stream xڴuX[۶>^]Cpww. /VN?=s~ɓC9s%TYQd 922>*MXUnv&6&JJqg5Av(["P@w9 t5QrhL WFSw5H"rrrO?bL93[5 ǤPy 4 )jjiU% e5Zjn "!UT5j>Ձ-?y +H(K2YtvQ3ڻ3+WWG>ff&K7W&%_ԭ] g[h0ntϮ>Z\@+K.w_bpos iL\`ob t0q0{7t5qus%{ͩ&9;ɡ/u1|L<{L\Q\@{?{fLATQVJRM{90*ޫx<,V^N{J:Y ){\A^oc:<|şڛ92k8X;e%]o%:fV/Ĭq9,L\~ ;?+7 w&RO5sjr-A-AϤW.)7;;E{ S64O2aKr7/'\-u5yQK;%3Rv~X9ll{oK3[ o7{!{0ki(~ԡ/;I3% `l l6z,f& `rFlf?;Y߈]]g򿈓l{_$,,f@V3=?{l@? '=? /ߐ=?hGv|`v7d{'ϭQs<5w,ޫ9j [=& &֞z,C.$[L ^WF6.+;;;c6N5k`ߛ_=f+ 3POӕДL_p咠V;$ vɀ%AJ@e ӂʴ)C6[SnUDL Q$E54+hOt9fےڈcݏqlѯ+ =JY[0<: ;]?b%Ě8vhbfhoMR+ Nw X8+(e|)&[RS,M`gO.5;!kOtC(Z6nEGR% "{嗮;jv(>+x`:|fEB̮a-d!?fAa8HtSde3N`C~sE k/ d2eK@!Wt*Q]OSXscw;]Yk؅o7uʏ:dw/ꦄ@l+G4p#VqݣD_U:wGЛq4Q7e7de _s=JWp =2iь2|KC4O&J PXל꺰EыLsHRY[\l9Veg)ol*I3孽4l=pDU>9/V?š,hSZnKW3Y Vx/l7\BYk{Ҕ gnތNdBtiZL( )B!ßЌͥb.BlţyK5r[`qYw&WQ pAg}Xy2 =̚TA+l֑^B}g͡xoڬ]PG_xtws3H/AZ_/K:M c`+W晎p+-)|OA1M̹@d_FќZɕ}AׂazePOB[`qF!&Kݻ"^wB+ZMΥ%Vn!p:Z-n h)"o11=`w-6=/<?n hEϾ)UHoӏNO`VB7^ifqx%g׿۔9w2 ػCX=ۯFjM.Wߥǵ7f)dWT Y37Ee6ȶ[-?԰q 6Zfz5a&]>/~6+:$=dk/Pu^h_rb~^K/q)x8|!tvyv{{ä4Fp-GLsG7!l;StN>u 5UT6e6{'eX'$!coJ[@qΗ-V ut|"(v/U8or拆0NvuI[63u):iC[̴gR"k wؙO ))/32FG̒Zx;'DҪ@E_P`xsĺ t vH=dg$XV] #-Kw3]?yo}Hsg4p$7Tj ce*vR38䨐  MrNSS FYdO0M1(H760aB醔ݺ$/Rzq"OfmjN_ g0Or]Vv) lma9 l!Tb|2 1p|owLFL-Cp'9>IQhbNUJBd HҒIl'i{S~226Gy{k7C_`SI|> |1oKHhhxWseG<21f斵9ؗii`6OUyH}V:+NY'3텷o ϳ70Ox?R]aX4Y|~;OĞgQ=&TBW h YwOl_b5b 'ܵd9S1+AdL=A۬,p3nL٧te\J<^\%DI64&̜ l{V^- 8WǜzK",_pbb@(87&}o?fi=4J>=n.j7ym7J$62>I' MmKnv{0 '-sJSU+ۧ7>0IW -.TЁ,M5gOS#X b$KaJ_~Oe*k}"bDžR.+RuDUЫ?pʪ_g>7Ms=ARwiV%P|*ڛ$Rϧ1aqx5/B1O%U%%Se!ūS [v83 BiaOCeWf=] [m]>5viA)$0*n.z"W_Ͱؒap 0\CÀ21Jյ7QʭjɤU~d:%ֻLhm 3%$ 2-T's ¼>U`/,}m*W8@l;3 x@@eDdNH3Ur*ܜsiQJV4 oѝkCZxGb8UlA YFHiۤ >SS׮ *z#Xqa֭8XylxZիi[Sߋu4^e.'Qu^qeT\F[9;`kUaKڃJ+zit$ β|lJepyap.{X~8`fMر_)[al|3Z-1JkMU`7`,ul:\[YJdL  = =p( o/L^Q>H\KzzJTCC;Lp"cP>NJ. E_a8u֙E S$.eXd.xld U ?͡q08`-uRCO5K)(O;iKSҐHu!n>o?y[jUσy&j [:p0fUC7X뵸EHw7Hy'6ڰ@IO)b'}8)}ރBµ(t Q$DC2(~L*")p]rF:ʈPR쾍E_HG9i(ΌdM(VEɞmK1>CbTRfGgߛK\Dn#5::0Lt;jA|b%lwN6y* .GWo޷i௩1\GN*Au_Me)u37k>u:g*?Q-$}5sH٣a2CuoF'*KKVN>Cлxn(>k~$_{D]+/lC"6spAZ˗3! )CB(4q졹"Q- :|2,ڃ"y*tRo9s42Bh~;۟hֶdMqdփwRּ=\ghfW\EIGSGل-O-?O]1uv]p(4}JQ~d38vlk[],hr.x@uk8'ŪWySr4~c+%sy#fm3̤vy=H3'\ CEkh&PM3-=:[ςaB]?6׉\6\ƝڲcOI,GKH zdZe[M|qEB"@^8rU/B00IǯR x0S#ba1 WxWQijo)'VpmQ!"<$+@znjO`Dϔ} yWZ=[HA ZɯIzܟ:"OIKMX>*r&DǪejWy&ڷpKUճ((6v$J[ukm3|7БWu<"sGXvY0zi"+N+N\Qn$5^P0Zܪf[Ȕ< pRR#L _1ھbo:}JDz&+az2Z(C:G.TFiQUYTXp$ذm53݇ڥvlWbǃgѤu+-1ŷR^7]V8MY !] UE^$)? DF]~;rb5y)IcU:`[y կ(@sovK$ud)eOI!S;pb卵Am'T4V\q~ۓ*ⵃyTr<85zZ Lpd* GlXU{Ae&MjFNG; ;e!xxFF Mu3FT˘8"U:,ox'SݔG,LϨ .`Ik%yEЙ ihyǽk6uuG='p]Ѭ(BS"杩2+ IZjC(ӣX{"Z&XάYK!M:vu).*'qZvG[&oG+zlc5se=o906ʠϱ2"Y@RliCI s0 FEHq~&>0踎ĄWIRO+lGXOH0k}M8ՉUiu a7 nLPMq5z5nߙIL䪒%d陣.pO6ڄĮ'Gx~] d:!ik}/ݬC{BO4j2*{iN-fiNB,ҹ9=;Ju|qbq?8γq-՝JϞ~*reH}1| v6xsp҈ W5WM7^  }]rI=p0pJu4i#6t v|T!?QnM>Scv˦s ڵ#[ʛ۴pZN`ip'%h*3|5b;I9fh D>&uMXm_olˆ"RHq}~8\lqW|i]nKOYHùzDX^up@U;RncR7l!e@/"pħٯKݛJDj iǔj~>`LNehRejMötq";]1ۤgmo<a=*{\MM4B3XtRUrJW&]P[ % 9Dؗ0Q޶tQڟ[?X3ţ !$Hkgٵyg}npiSa ]oViyuD3V }%SǡXKrPQdKk+yD0|D' jvjQn-"̚+Ī(YFR=sWnQoDBIJ~,/t) Iob;,w$.< }M2_*Ǖcr|*pvuMPhڧ 筟OY04qIA(I,B({K:$rJǐHJUW6'I31zM2(KOF˾Lit쥢o^z%ZG8]0V9P/r,<İءC8D 5 /8hkn1ۈx,/4!=l%n~5WAZsh-p"Y?wIUb>z En2a75󍈈N%/&J-G^t/<~%+tR? lD1mR sZfW6iad"&8Cv뵡s&ZC<W|cʪ#ec "*E{c|JdUP`c<86+I˴lfX.3zK:.DAVIrUNCuuKC NO'/zdm ;WsU  ;@H]ۀډ,#ay_P OԯY<۠Oyq!_ۊ&qީ$#uF*)S u2Mr2%'RO)^ ,NbRةwΗX3qC+[0K 9n#M)ܠqZilN#M! sHc}m%ODle䖕/L(0D=U9֌qPXB"%7 ֬J&Ӱg,(Vk\>\vYMq2VT4ٯpV#zK+!\vُhїcƙK0FFd9Rp3Snl!ȺlY7Л,fۋkJ('Nc]q1:`XϷKT BUАEgÍ:-;U[~HB2ޜH*?*ajr_w ^bHa9ƹ:>> qͺSm f~EPmŠ.ZxP ـ go>M,_ POƉYZƱDlCE^BKi.k:is!Pɇ>8:{:ۃ4$?Bṕn Ќgdm$ NB՚K~RZ0^UuEVHFtQHc>sG 䰴˸?)W̷5d; 18 BSښe{fCEoh\ՙq6@ߩչ4+㖮Ǥ Мq @5i"{E!SѹK>.XPJr3޸|z֌)h5+S0 =1+Gxƹ"?y^`R o1F)1Fv]Z)G}`R҆`x[U)5 $:1&9׶,uw8بRk7c!^7(ᖕp`d؏1E&zG&>jTLj'ˡm2 WUOg?OTߜiȽh0-aEhզ5s$p8tjuvvvM Ga|n~vS ts[5U, ݔDmA٭ UC*d.޻7Y^@Z' Z> @i  ̄n v7Sg( fqV"2g%Άyw [\~p&!-MDmiO盄 9 EaS2 fPSvB ٬g= ΰqR *.Zrmʿ'lpEap"Ouc59mu=ǽxre^{/k?bQNBN ,G#tuj)krml5 6Jp7:goUpsca<]á7#gH" kh'[]1?C--h^krylOHȟ3Vu9Ȑńhfl( 3'nKk/jxֺG uuk _YL&\ϭbt2ܩ.2$LuN>ankШ;/v^Tu+71e31p bAj0 |I{#̯A].j$Pnl\dOu`lr8 7YV*Yc,'2ӆz_\o#kMhʔ/mbׁ^0`&fnrw[ɒ㩃ҭjta!WwW l2\5~OKm ?Ee;{*x&Bpɲg;aDºkjASy.h cR(~7OCઔy߃}WR8Le+X:?W,4|b-.F>[IeW1GC8] }L,' kjc"dB_UQi`>Ri3Br&3!'lPG!$e>XDi5d00?H1igK[|9ʵ y2F9iMm\: th+y_"}20+FޚJle=ŷ'r(I ͭC _p(g}`ִeM7a 7ڧ*󗉲Z鶠xG728;D$nԺJ'#7k4id ILO ԳW6f<}⒌B _Lbk>lvuU㋸rNj9YNuF'sx\6R v+4P"=97|3EinM-0_!O`1Ҽ]F$cl9npY !$0ιAXmTO6pXBs;t'h͓!^>6 c.HL~dGW0cBc0jt3W ٓ/ : o ?{#~J$+!ao=yJF1 ٦_$T+`˸y;Ӈ9dȇ\'ׁF`E'֬r0*C+G9 odsq; dR˔693N-D~65$Eߡ창M^<8hСD:+)Q$x}h, ;VNNYaO{g#fwKbϤ+h[&^s27X> ta g >Ӹ8tLo a.,UŸ@iu-3h$aߜ_BbjT"to{|jYգgt?邡n59> nlQEm=XUO,Sݰ2ߛpD`pAWQGT 1 ,;]YQ 20 3Ta]YǮ2W[vwt7% f9E ,pv6#;WߢM7EK(dQʲ9cҪ _ѓhCC5}G+tyl?q ǕRSʶ˂4zD198kB0YJyjab8%}.P^Tj.SZ1/35 vxث(tݞ=FyRiF?ȒUVI,T~kٟ(Z;&@==CSLD^wdW[jG8֥;;"`CGs}_ -ً 6bYy9()Wk3n͚?B "W0()/8w;L*PD7o̦рS=S4 @Ʃ-.B?̭x0gxKv->@!8,X\j!ڷ`:<}bunyb}brRWUvJ'02A| v8u[ .h#GТmG aylN_|S}ImWILT!IuTK0+R$Q _rvjئz>}5M7;M0rLPD\;/^hDƸxifv\oI+ԡi%&_AkHd`&QZ!ǃ]S>hp5oö/o;lg_x;QÒ]!kQ"QxS 45O1:M)?+xMͰ-[RSD5:x?:G];`&V*/O-YMMocq/OԭSJ%2ӚYiC TԌ)'hi E#1\yXSypMG 1 ,-"EؐUAI|[X21*)fqrRJY8f~ζV]ȑ30҃jeSP"$)$B3nT@횁1cw:K?]]`\ǤPLI`41KAb|5oArK, l .u%?Fe((ypKI~zSw' 6G ݱTg4ZC8/$Uql4hmr*d jk']ȥa6f :jUwqRd1Q,vCĐ1&E_)Zqw :`o__Dp`jxd+%6aN[f!8tDxHq{pMUq(L߬ӄc^tffX`kT[agIthY WOL Yd1\p^d<8fpˡ'ecff+ln5+- \ņPɶ'b Ϣxx-,zUb %p32^'& gܜnFdMA <#lcU8[J+mZ5VU}[/BWү\Pup0?M"-a_%}upIUX,AOHDO"]M%JHKW+UqKwFOd.%Dk[ T#dV?ע$āM7GV+>e}mm B8ľ~0@8t AO5BFO={cέzηB&kxd' /:ߝ-„Z46O6{Pvߡpz Ƥ[t#j1L2pؿ=}]{{eb? nyQܼ##ð>QNrQ*$4Sd|xWuԥiNt"%~b+h atڥaV ˏ[8^Wf?=n8AEun_viCא`qQ+Dy!;䔽/*C0DQy;3:zoTŋJ$Vchћӆfe-R)eک%[MiIfnu; t9=I~YN21t&pfjdÃR 1y}mSjC:^Ze G|lE$Ex$f:T^rV :ƶr2 8⎚4IY=QK,_ug$BzMJu qN0[s?!\;=KoRi5;_ Q,FVsdqP x076ܗPhB.vWF8~BØ: 0%I XBӜuհΐw[TeUkLM*`歽vT;tENkv/~';4W~ڭ3za=íqɪ"az}W(]0EN7VDnzv6w JހaC;/,JH++׷=9ⓞZ1K>|_fB(6RPPϺD.ızP[CƖ |'j@g wY]>͊t AMä\LNujQp+0K ]x$Z)ߵxs&y=:jxihGXݱ78`q>0;exWȝcw"DiDErT ;nV0E>6<!}BuљeiUD]ރ?YnVsIj,'未.__a#|Rx؋Ao_9YSK{nClb1\X]et'v2BV'WTݡ `KPITZTf pm d0!2mn߫`Ѣ7Iߓxj~)WaF$фUXQ72^rML e5쟂 Y,Sk*8nof9NLW\}.N:/i s*%So> Ҙp m>u0»[/ųJDx,J63Y}aa˲xPl J#& ݫ\eBv m rS{-VօjW6 OBcRc/YL`vRn &L^L aksυN)Y,U<%+H%A0v.B$ȾD80cO"M:8_n!#ϡ{_p* siA\[i*0ao8GVw4~kw[a64jR$+-X;"$&QJ}'|T \X"du.⸑*=\@2mAiBKe)L3~l6z2qԅˆtG=R// TWI$nAX%Ȉ{j^W|nϔi bpE?f ,: 3_|QPJ \><sr_kȧk}%42~QD'1~t,^qX3ƿ K|Ib.Q&p_ 8͡y޷L4/l2$lჼDiZdFG"XJľH 9dQ aNwL gD8?E /lA(S4#6,iUBwx]EpB,g. -UR; 8ĮHG1k,%N!q3 EG\= #2γޖ86 ,.64U+~~W~YGU2=jT i p#sEE fyL:Pz_y<"q' b=JkhH_h'uEG2yl̙ .PDދ _=rvcyKx0};uk–+xSrYyOeb[RvCqD}.#O"sC ԬVp@K4ak#P{Vt>Mnmv{a]yI:Cd[@qEq6u,"߾Njogr7]@,}ƈw͟ǹ|(FĬ6"vh.2DPycxҹ;c phFk+f^:<p•͖J/$ثiDk֩ 36Aծ`,tQ3FHL*g.)z.B{࡛t = ԙð: {2CPՒkF+Fvf{y>#٢cMg4$?6%[ŜwOQF"(&&:p1b~Tv٧>.NzԴn2<ȜKup?ST KJ 46l5h;HI{ۣ;qO]Tb!'5opc5}vƵΈ]DwgZ@a7bZ6ɶV{NK Jު Yel xTc*D ~?9bѼN|$9qtyEY8vÒfQfn6Ph*UKHV>tuoڷ&FNskL99(#Z> 7<?"~TL٭ \WhStYf$EC蛚9LVLRQ90`-T7`,R:}5-i6T^MP|UQ&9|8)[<ؕ9 x[p`X8t0  e}y@R3%gv6!.ѥ; c|cxnyoef|WK](ko- RRZ_L\K fKNH`i{%\ropğr( P> >4q5[+s w\᠀\)y ֋2D6PJ[|ż4蝂[;NwvǽܫruiMҲ|.\vxP#K.A"h) 5I!7ݽ{p{~ʕ-ƅvC:Row{ VXFMkݍU0V˂YdX1vuȹ9g AC~e#dDks|):ՄY*Mn)pXoC^F|}&'U^*,XR'7uЧ5h,W~&bmepx&t<,y2GMϻF$բFc-|r4gYKZW zH@hoHpk:5;͛"jz9Τ(q [J9eiVIy;E#˥3W/Ӽ');G]:gIn4=i=ll!h*~mnOd3D_ # T,nLil" fCIH!X9(qO?ЍwVXjx.}"%݅7F8 sGC٠Zw7#HyuEN%9Q_Xjx:ϒ'@۳j*ĥ*jId,DgIt A^Ohl,;^(KwBX'VF5cܓdE8 "I"5v^'IfF邆[Ũ1׽äsYWf('+ ihxWz4ʽK gJg1</K/027Ҫ{Le8~oekzc@0[SFW:Sh:sʱ^dKS 1;@#iսƍQ-z" ;\آc6Na7h&io4`!E_ZS @$S]lZ7M~&%L9&2"Ro &עJ+.S" zȨ saEe'7NJƂ:"UFɉϣV!ڞD n'D6H4ʟ-.uR0d[ͣY,v?uV/ Oee5mCNA_YCR0a|dzm[\VҙސxځN{wZm#H"'\$=GPWO$qLT?*\xŰ>ETxsz`ݺu 'o?ҭ&z96vJA/Q`Uⲹܥ ~5%%?ԐUl\#!mq*r'to/L|-f'r.KdlN{ aK7Ib 5|thjcu2e4fVqڠBI(,^-'3“cby$uCT_c(e,zlo= sZ/wdB~E"u!1H8 l$P00X u-L Djt^O֥e҂f}Le[`p>yg"#(?Uy6¡쬙!n$,{^* 7m;Àz0Z M´1Acuj[z% ɋss{IAz@ m@΍\m~PWx-?LiDD\uL'`%b~hyLf~hUS}iLS^l͙Ivm0aQ;0>Xe/z"J(b66}? mp _olR Iɪ0W ӗ- "YzB ͊.r|& ߅M u6'j\`.jdEEJ A޺z}qB](<*Gu'yO֗^L+̩㗆x퍨8KZP dzR被؆S<|Ou#NJ(\妁z@a, fͽgc~؏hQ "ym8LT@VL|"ߠkPz<9РP:?a,Jht鑳+c 8 퉶Xi֥K ~Pן5A|bͧn5B XBf'1.w (+QBYFi{lxw4 GD십_~,S!oyhkأ A7 =FλO4b5ptU E(iRMlqGB͏_gP:P\?\Ys푞U%{ Tl5wzޓjj'x8nVDy|~?;;@WC"7##o_3<$054MlLz e1)Sɕ9-ۡO`ub>ؾDw;{珍q0b wDSN!Of̺;,VrONfƮǒ=~eO/s |0 sPk{Q%yQ^>(w Z[IzrkNamBj[66࢓J 7L@06G*!f:QZ*%RD;3硓?#=w@+28ZX0=&BCn™٧J3 8ӄJ5'y7 a<@^`oX{f(\%2ǜ*ΐ\Q#r|3{ffqߐO6Q%9uz*\])6`~uS,Y.wFBl3iT>6 <*uӂ63;_zڭXLRR2INTE4LӦq@u$c))n߀?eu9͈_IN17 Qǖ‘p"+yI^n h9n( !ՀGx!>40N\CIYE ޣKgrT]1v:c6^ F =C 6n,rxF.)>ϖ0x0> Sxݪ N~92LpGR{ 0%Ȳ+I[P/Q5ݛ_>ZJ؝6Id鄙g.c)_Nq%'_Cm=nH(TT׋0Oiڡ ǹlzR9xrNvkpM<y7. wR&9_AX\ Ivxoos(b(g^ T\uhFNKig$N '/Qmن5,bI](~*jfꦇƿ!xiiXCEzJ}mi{ceEf0h%ߪ+%C`?+ZN1_bL}hm'g;ˏ:Ϯ6~ޢ.hί*Ҝk9pa &ay"n Bvkic4 0/#Vր*v2Є}d(ϧRw5v_D Z*{9Hd'kj ;^{;{]c9f;~E>yrLZ6|/I$SYl2 *I 廍]F=GeTH(jK.!y7mXGhTaWGMmÁ.XMPPI댞Pf 3:O ZkVOEkLZgfͬlb9佊F/poחzx_E~vh?¢GZWQ! ћ46c ȪX5 |[=.xE=R t J,/'RTaD?mdg߫~Vа~tOO0iH y,ʁ,e=&gPvmᑮiB%ÈvOHS>tO9`Cz^Ny؈ʻ%>qUu>XuC*~dIjbvFv @O lFB3Ǹir"reh Gsg#6/u~`Bvtλ)Dv5ַw'mN:Hr%N*!hi9tяծ%W. =X aqrj=nDSہxP>*= ǒGz{]7us];̕9 ?aԖfShY]o2^r5o%K=I-of$s}yo8ᦜd ia*cgT2gGgaTb$ؗIײ(ħ$"`!&q%"@HL eJIM\@JwKۄi~gq7=;ӚZ7$h(ua__SM償 rEW2P!lN1Nwep$Z`"p6/g2>'ݝ#(,y|238t`>_4'H endstream endobj 115 0 obj << /Length1 2373 /Length2 25437 /Length3 0 /Length 26863 /Filter /FlateDecode >> stream xڴUXZ6$ {pw!{g@UV-.&WQg W&6fV~"fceRZٛ:ؙYY9%6`+j P6w}s}`eCA@7 t5rhM*`W&3S75deҽHm]`b鏷83@bg0YJ`7  M-`KP.QSTQc~ v?.2I1% )P ߊ˟lw͟Íojo`h]]YX<<<\\V̎Ӱqxo@{_qY`c8Itxkӛ?'.@6uWAEE`jrLAon.do@"H9;ɡo82{_S=1S?zeA.6..Xwsf6dbJrRL obRuxb o `gg BO$m vbrKl܀rg&B[ftN5˟tM1[|}KS{%puvS`ac6or K0_7&VuQndZ"(]{?L3u/Jvv0'B.K,j6b +{ۙ%sm\&6nѽMK|}k]q  gd2-l@Vv.nsq|FנXA`7/0,bDB</Eo`a,#-Ro `{3;o55 7S3++<? XZrqC&|hwZ{9ZAxfVٹo9 ?o`?m-n''Iu[#~m[No;߶?&u|+հEu|Gf{bvgo-r|oV?[9^o S;Yr]v@m:ahlo_P-.ab0q@rvj7@O9<\ 6)O`OXG>f1}@2w(RASV7K Pڿ&VM\[n)Ih1ke(.wQpNgŵ4G%:ƼL0(o[Ƀ(ekq\ $^tٴOl~$? oTαC!%_C';lĺ'R&`Qh)qY:i~Tv.K~]꤉Wch>iTHM$ EN lzl8 &?raVu'sS S)0(ZdȕgM?W35RhUge[ޙ} "qr4m /Ep1;lGZ'TB31={w#z\ ]jeVџaMޒxޥf@I*"rf-8>ȮP%oyt; noWJoUf7Ҽa}'Bš*blEwrx~ȆϱZx"z7l.1ZbQ0E:+W2&6B?Ipn܌cU$\fXvJfΌ_> !?1,;QB-0~?WM <>Û]&8=UkԆp|Actb.io8WC i;^$[S'j"^hXjI(B= F}F u#JAKC]sx~%Oԛ&w]NE4$W'8"L׫G4/ȏaZt|߲AᥘWU":ɶ;~f(j+ݤs§O-k=0,L'WE q/^?iD5`Ϡ\Cl3sFߡ6Hx_ Hf-q?S|3݆O8Tgu原\t27J5rj2P0,ޑ?J @14)I}'H|`G1ú@:nP@rLGZg9ʂnrfؕ5O'zm]&g=軁9T1KM~4ãl=zŵ=>ӣD@bijM o z2~TfcO;qٱ6,8Xw_ZڥtVB؅s*gh\asFr$か> PW-yqd|˫^R)8¯> Tx`9v;,\= "nncXI|4֗R !ޅZFTƨ۔A=m]ns탐^5MEg_;kchb5_~!fk2$$sd_if?E(Oq:9̵ pI0-|? OѠ7x{6*`h1U |[T UVfBBo*EՅnP q@K[ qnDtp&5j[Lp`R?Dt쾳٭f*^עYJRTdJ4'X|R@cQOļUf]ޭio 7Y$vݏk y-^eٱkiA6a.{+{w;5rt*cG\ ` ̐vߑIdu0gs2ؼTy:a6[FBVq]Tq=,54u-Vep@M:.?7h@˄4G"8+6֤>biNҲBȅ WpF'[gǑ(k&GɮIhD\*rbX͂1^x_c|%qe,m`ѦexC$( # Zaµ2LJ' /=4._< <  ^=,eI[qA#q7(PI!J ˽@D4qz8[ )F;@E"^Z[,fR~7Յ;BOG|kU?y]sH&\!3%,,ËG< C3N n1\3NMah tciMy# :KxJ}3[\xՄ%Ӽ񔧯$hHX2#[}p"6/*Lw:vU|D-!Y$h=#gZWam(ēؙUdfZ xFe*0b'K:GSd^yFhcB=Gݠ@D|b*UXD':e~jHx 欎X)weYxJ"xRꟳfiSGT:a 쯬_q<N8Ⱦ_!Jѻ*rP<--_\)Q5o7{!DՃTj]bR*ƅbEҔXҬ^-M>bZpQA Nt>w9b޷"|_fBEA.r_vP{F`N*3Р;t.(s r7~.޽.w`Ã8]Fcx=V% Yhfh2X=yF!}>fIc'ދ}chBОU)7؃%OBU{<ף4!DKTIVPu6A8]I}5)dw:UR"&pcbOdRBZENt[8c vCAᙤ/m:81jk/9Cc{!Mч7gU˔p=c ڝ7U5/KAڗ??-%$&=KmSeg-ik2,H 9w5Ѩal.h.\`zIhK5]N#zB3|0 ~08P%k}ۭW?(>=TF;Gp+rVPb7P"4M׏IL:|G "W-rKUto~0\Cq9p_Ghm|&! 8 OGU?~y)j٥BK-eٔ'Ϭbx9.]j+&,h./c׋!DiU,r6cknqHҹ x:€Mp-/}()@.0Aj,hTSDyjː~n&B۹=&wDhuZCK["nw|K }GF4dT-Qs 쟛r0zu,{B/C=o׈tԋ9U4eqUw%l_WuL+B.BXti9mCZ}7[v!n;1ߑB. +^<ъЄ+!on_]d-jtf44a&b&is rzbGn9baOr֥Әtz(TPéC{Pq,ǚeibC뽣)m*iмJf"ێ}(  2d[Ϝ+_7*Ȗpr@'U1T$J2 1*M˚ǚuvt^ﰲ↏AK"#;I陔~SGZ5"ʻĉs`3REy%~ofcwPOz_jP7w4:dnpxfa54"vk(>o~10$P&QKP zf ?ʪj+<&#xƿnN6ExUi K}= ʃq}0Z `af'.BxAM W+W%jJw`Ȅi,ڐ}0VHe]f?O-_I6#DTۧoW`X4t!_7\3Mw,MTcō8GE{&պy$S֐&v }KanLrvyPSv)# %uGOxh#K % !V+im%icDͭȫV/B%oƐ{CwRe1G~~wQDߏlbydykĤa#3.b=ӁnC284 UУ(DhcFR5% bb[ѡ%MAꑳ -VٍkObazQ|G l$R^&IJ8"$=*8+Ԛ+]=Y*#t=8;kx-t%N 2C:hdqTgo}D8KYLfDS㴷f4L鑱t Nf.-cNx9- J gcsԿ^@iSMeOnǝKV q{+)sEqe')RVJ(,C;OE,n^V]aK |x\~`VA"EUM1tx!mT'4*zOy2zS\bPU )@ Fkvm ,Q#-gv*_e[m Ddquf3:)(y;s-])vD ߧߩ427BpJmTSǜK{*\A=4"mP#lggIvќ6JQSVUh|![㦂QÉ=X*<8!ZKAfwA [Fr68 ^/K"LEN}ٗ#5nՂU7wLt/q)оUkю".L)Hjy]4۰(Fk~v)+[$Sߨ |?=0(O1ȧ|y)םu+a+u !8[EElx7[x bYIlcP`3`{ʨ%ڡq]=xVgSr ޗ)!<_e1;dYK9itVIpr Ycޯk{Mx}7*j YxT S?}'I5Z.qo${S !|`n&vhbjw$ա%ϯ%gs <M4D D,7<&^L[bὃRm{{AZ< =R_+6'Mԭ ^ Zs{I|JGpmcQDw+A=`s$gؤ*1 g^&znҫ}ç ;nSJ>'iKiy7BQP3ia%"zn2[]~sħJcE=֝׺w}N n[JF"NEF: MX)|>P —Xr/MޏN'4AݎK";nXWС. _KsTNE"FɢQ7BSYv S?J<_R7cڐ1weHvUx$9{t:6*s Lբxs!SYK'+|k)}gw̤Dm?~89&|PoLS^By?;7М<Fr̍Vl$6<\rHoƜyivI3-meпUp2R+OGk~CI FC$G'!']5T:1B |?7(TdGy-rW_B(E+5sU?waum, >M3 {k-Pvz 5{L?<Б<tA.jdeaWq8F/m%QjۏQ`v .1#VZs׸aW2$ mLN÷a ~!ޓ怤qJF|  q3:ׂ#z~܎E3ZjŠ ǰO"r-AxR;/u3ӡgܩ梜UoN b-G{cp+]k6 1sMz6k&':}, 'qnn;Gtu<3m"4nT~BJ} w{U&=D"6P}k>jDX~֩.ͱ~@vPS\ JQ %([ր珽믻5xYve4Y Q~kNny+ENrF]?^Ձ,6),J*&^'1ؾ״E Zm*?Z>#,4_#yyiǛ5;'{=Z~.mS4ôxr.c%hMuѮFc䐼mYG:CL駍ٶg ěRr󍇓%d?cEmj'gЖ3o, 3٘ }~͍jNG$*rE=Cdj+gI-SH_#4Cq.<s {=S(" zsQ JH/eH4Ijl.ù؛pҚABQv8l=ˋ`etƁf,KG6EnCEef:[\)q4s􃲮\n}`|R ycxߊ9 tO^xC3;fvxT&!rg>'rZ<^ٝ;J3,W,J^evC =΃g#ЇB `쐺G05pHȇ.BNzj?}LΔw[Fm{]i]f/Q Uu,mǜ@ROe_RnN:ǣLSK@ UƐM0~ /~hM42fMM\k,i^~\@d9^ (y !Qt>X>R$`r!#f/m;BD|̏b6~WcRXZb2mοNz6 t8cf-t\Z8SCe?*|~Y7r_G67n3b}4Q G42*Urω0;/J*}U=||?="SAtأ-ѣ4t iAfYM8vlx dx,i-K%h%fϘT%٪VQ=zhs-y2ACA b3+*d˒6wwA$ |A5irRx15%& PWgd,6 V_,ĈĞ$'}rދ*-†M@h.Q2bޙ㕮{Tk- mt}/nrb{qiGiN7@"*$]XYVc-L!t5A2s|<ʵr08v >D5yyZ ׌X.+3 oB6cP3~7IVkig5݌5`P3#CAP%Yd|6KglGlfyHs6wR a\ C=tL;^M5\?>D@cIhD'+Rb{O+MW\<; A1 Aȑ7r VH̡G6xho>'cs5}%zM@J+)hf x"F9AѰ08G,G^=3n!i5q6c#Vg]15 4דJ=Jd3ɩfhVLeS֩T(;U۝ 8QEq2Ӱ*r=K?_ #U Ѭ7- aHaܵaF]ebTx)mnm9W"ZV"Kt0#^7ޠIlKzK笻}~(dOˍ=j@Il;¥;X;h0S}0b|y!}EEETR$Zѐqw'w+A)>i|Q[DLYu6򗙃Cm'nuYN:=$w߉o/*Kin4IyCyhKYC<x@Q3|ZadT?B.]f!X ^7Px\E̔i&'&'=J|"7a1kio=W# +͔~(6֛ES}| { tݛ Bo( O!Ge]`(#ð\&;4rz_W;󃀵\Y!<Ύ+E&y} CM7$ԗ(\z$V 9Twz1DnA4ZB4Bx\NUB/Xv.@ZNh9>dqmώJoj0-wžga:JP(sL9XŅlO1d=Y\ķJ:a, LA;Gvw VۙhDZMcVbP>/>kPt**2o$bz]:A2AA'G-}+;-NV2Ect!]拴ב3޵苏KuTxb_wm?Zl3o|b I& ^ i{5)CNZB! F5:,y"OFObwMFrfH(>(T:(]#Ŷ )xM?LF \t(;Qt 6K୮œw_/]YAj>m9*$\r橛ɮMKʊh1F(x4 0P5rӨexd"FuC _Ǝ=x@xnϿ{}0kp}Qs䇈IYTE$暞H0a{^B/aaA|d-|+_DA^ u;W,L}^ݤ1:% P4[zSvp۾.56iH}$1W̢^~͸+mH}PMu^R!J౵-{(7m"рZm6|g+_^! alOR<;wJ7zwLf4Cmg5"lD~u9e μ1e R7~0CNUFԑh91}zJÏ~׭Q=Fք Rt7)_Dt_<+2s𠟝j67y` ;RYGQ#eg7ƹ'(6bBzH?k|0`յW'h(BDT~}#zᐺ2߾>RdW-vJ^ (%_&mC#}=QV7:j IWuPK Wm~S,OICKA@X(Ze-:v;j,{˘GxHfVۏqfIY{eȑO*<6&&Q?`^}<Y; Y-t3cѮd$IbuNSH?Ô&zYjx{ffiEʿ``~L$S:ę RI?Xjz,A 7ٲYR>BwPhcD.k=o/E JDqy'Y bxO,Tz<ߏ ľh@fwl0Fm8tdie !Vf )Nwt1ws6Ƚn+[jBWgcGġS@}UpZmdUJr(W.]]6min:f$j~k&-7ၶzn366+L@@/} muh+wxUsU0ԽޏW&cwԒuqz}g?`KS^3I(y7=zБIʭak刯ܾ2T-Y Sf ^jpht"gmtI~Ml`qrF"/M||[7=ȈuD.sQf/fH;| a3w( :PmbEј4m۶m6kǶm5nl}:o7\㶂n-L_*Nu gj~iWJ:Pc-bcfuWeT[{ GQ2ӊt".`$3)U5os+ Upᾦ,"vJbHK|OvJ0)LIӲ4Q\8QvMh?p9S4B4;0,!'N' ٕJfpݟ%ؼzk^̳JGm<PvO$=[KKO=B?oұn|0ɨbN`5!q $F6EYd{>o̢z`[<Ʀp~+56i^A쌶bH=Ӝ8 V(_qJv4T֌^?e%kӉsH?Mr!8OFvg{`4s 0 >2G꼸{\H״ aV}=6qzע8'[&|[eם?E ,$._WE?ޛ b)ήC!TE,wi×vWP3,ϔ-ls L.^〉XퟺB&XŇǖdna{0rS `$%OE_]3hZOOwO.πIܾ>l!›ҼZWjf6rtvHtO 3u lk#Suf5˷ g8$%B _2!1oԶWCAbmn+RwK8O s?"-LzwLf\/k%/|扖/}6xИRH'oPǐdwWi&P2F3,G[sਘ]o*!J V uFI '/& JZ*`Y 4qz#mBIY=0N}}@x^~z%) )0[CꀓRIIUVɅEz1XD/$f.@)UOD4 D"t[Ljw;6?ʻl=_wno=>Ȥ`#f]W*Sa=Tq>5.|_K՟}Щ).4mrh!yG-\\5Yf$+{5mHJ(u1Im-͜>?Z]gSrV j2Glp/4ƛv]ծBJ\+Dͺ;kng e 4QRAB,ށ/dž 9Vn8Uley? 2% GT_3yzb`'i5 o`哳 %a k9Yg&\0f xvX^w2l& :DZ-ZHOqfG'f"E5̀Ge8> V_.8A:&\bH~8$_WlEgY\6'½I{rrW4pT%5L\zGWHHJ'pQ" t=D (].p]$z4zyf!&9{=޸,l˚$&$==#:|fm׸U. E 5i憤1]UR務v"PZ0#xpL&e0\%aǀ7}moB5F{.~X@5hc[s|wUx_߼PJ}@~`,ӫhнIj(^8@OBG@GU ;{q)ACBAj:DKrѷT5@q4̈́_g~Yÿ__yY:YwX띘f 5׵p^VYU6(mf'-%@P7\ tOíILZ_@ 7=pP)^$)fasnO{Υ*<3uDkqحson:-edě.ihC~T_%#'Rc_t$1*m;qH>;J;S/1گb1żk 6ؑ(K_;E~`.ɰ ōHgdcH$ 9$9+(nӘ=(Kɴ9xo>mdhtwS`$&qgB@ִw71 |]303R jؤ=iw}"l v[cV@#>/5JokPLc NgڲJ(/ӷ_P ?9ybϪ''4,7Ը14\"b5^ F͎ *Uȭ(·$n1 "GL?+q:Nj*0x+Qg %Ҽ(Nf4TMX;8)f؆[?ՌhMpg&d{ƮN#€I =Jn4(+#^ߛ'0}U`t}?4̚oU)16'FN8"SeDCOJ WfQY YzdhP2PdMeNY;+q:.t7xaAR eY:6D6ydxAddKŌ m ҰwDŞskmϺ#մҲ YiR_xY})ZٲES IMA@r@q+R5HW#OnȻ N+o[v Y0Mk,MsZI |+-Tx&a)bLp"8-t~b[!l9Lj4ָ3Gy寉lb<#iK3[J%%^ ׂ vTGdc3 DSq[IRxnY+e`cf/i`f>sB7/@s'o.S5K/tv&8PNlBu0VM34{7J.?+_ \r@UuME M{""Ǭ^-0wB< ;8Iv%֡R(8Wߡ`v!rZ1'Ji6EM5D_mb8Y L9-m|nlDbjtt<ڢJQ뢠{_. ލpp|$Ye>$y9McƓLˆx}0Bl?E?Ts]bB4Z\4$xmS,*PPؗMx=YtENJJ1lWGPq<2h(]?Eכ$4XrJ2>L~ݧt?( yt8{*u޸jWi.i9Iѯ`+ŧ)շ |.)ӽꩣ5ptF=:U+Q6˒]#tKhL<tL=#|DbeW{#S[Pkonf=QMDzĶ!,圢JJő>p\֪ &?S/)-0R}6I)u)t"QOw.[Ap]N"zE#%LP|N4uqACd/<5HIQ,ش\C#uL!1Iybƣo O&@>^"guWIJCFzć~-pd)S(M xO%g21)W;_ŐC5/: O5~u`/ՋDF4$AySỎɛ9Ŏ\CfhoQ<ɷJ ^ .WVӤ\GF~WH[ ʡr yE)TR<-u9e.Xٚa]@O*,Ԭ{cBa4mpZf~@ FVK]F\2D۸❛cAl!h aճ[,e`6x?:. ze8S;wEp 9?)ca`P)q h9ZI,;3t^ GZ| eF ,DgDu3ƧHn3t5c/?MQ?8?3RtAgpF lŵ'>o~KAE2Ŋ{<⋥"p?lQ^ݚ_r'_~ ,nMAʩ7:P@kNJR@0{P^W+Wu`3Msg༨h*^g9neb#K 0  iWo*p& `šSH%_1nuf3I0 9ꮆ`,KxgI}F8oSIcH,FbhYT}lJkyuFJ$FD15Sm:suDx,yYʼa]gm "_uir:07Anf/>r@xG ;{6_zL3v fh *XO JFɡScшӞ<)vG K좉ӧ=up=:0!L-rɗIe$<"t½~dOupqN kJHhvSeS $& jܦ,7uS/B_e0^Rn#G^yc"]i YڲH|\3x-QU3DZ&' .SְL lѻ˷D<{5KhwĂ#k|T##)I!v!m(=_qkj|XD6"b )JVLQ0hn|k=hP4A]BRWvMAttT Zs$MPM~>:EG]ktvY[]QGub*5=7&Y6<zqB @MHsvN|ی.g8d}T;x8ek`*ꛄ&b0JȫAIƿ1[>%3 {pݸIjԓclܜW[G%jJ z hOP@ia)>^ߧ\x"V.sZޕуͅ%1Ik61F st2"k!"흯򴸹OMWͅ4Z@q ؊VMU ۭPuK+.:IW+DKMq-4&ZpOb␓9s7Μ3χC(^}[Qo;_8"0_` X =#LbDx=W]`9drҙʨJ 쓘^Eb:j815C:JYO$5łAr(IIimhnV"X^ ڢbÞ= k^|?RyykyiUaKw >oホ"lmV}闀}n Զe.U4?4o F+B#F"|.]2PMڥ0RXٲ-D9>Y晔"O2$a*.Z=i~Pszݩd?+bC60I~2w}cp%Hj2*,=/SZ K0\o<^5?C07꿉孮y#фg?D_j9$GN: l$5soղC'Vra L7 gmjJ G2^/p?:NvtP l!+\w6۩%p/ a?oV}Y;>fYvmʆsgT+ `}_&Pi=o݁# E@pꘞ8o$\~d  RoΚok_z-q0$0ѥu9N~FI%vcJԨdp$\,$xg,9^~ey۸SFqC?rr?Yj7ϻxdIǞR? KmT}}$f *roD?fc]KkcbC9G }bFExD>i]qWO"Ψ_u]6ZY55vhk{ger&z1XY~嬲ebo8;!܁#Y3OqIdL.2ZI?q l3S +8ܲlR!le DK\h0i'_"hBT圚J?iM endstream endobj 117 0 obj << /Length1 2166 /Length2 16076 /Length3 0 /Length 17416 /Filter /FlateDecode >> stream xڴePk6k$Cp݂]wCw}?Ep^w QTa25ۂXyrr [f)'Ck c+#33;$jp9= 4Nv@_@щ] Y4."vfNb10[ mhlehe0B -`476ؚT51e {`g;;["&AWj*A?y ˉ ~QcaStpQ3Mjs'';&&WWWF3gG'F[3F;[8\m@k_q ka 98Kiww7OLJcn, &T"8;8!o{e:֞ކ{b gG69Z8:9+"`ja ϙY K2ȾA; F'7}EOVVVLDlmlY;"i{lܙw@ db&vLj {g=fl'_GGoO;[;#h898=o00vzWt)-_w&VPuMi降-`4E`uznw7ROG B*,-܀&N5_eY ,lҨQ~,lw='ާ ttprh&U=Asd`:8#0+}MnM dsv: 9wbL"DB& .߈$7b0I8LrO&O~*#o?dN?=?@v ;'sw;s 2wVLߩ Yީ= 2G l+{{(8p|7d}gN/g%o{VUl&:9Xi3/ wϿx ۺy2s9_OE7@au֘729Gp _S:j5ch.)Pȿ/VVG'TImٖT=}c$g#C"&4Ψ)WMJs$[}.=6~,:*T{>kK 5 ~LG(~Uyqi4-USLx~,/G*@Óh (뢖.θz H8Ű~ 'iB\t ^1}د؍ ׊nщtDJPYhګML+UMҠ㬺m <4T x|Tg3I|Uh_)}ȝE.o6|wH*_m#TӶǞ7qJ8MQ"C\*'ǵ[>؋TO ~o~slHJb<xɟ3eizl,tFgMZH"j`r fwMjJ\H -^+o $M nzlQxj1D y` ΐt:w*~>L*D&x/zWߗCtHh6m*#wÖ76dþ3 Yuʄ:])s)::g$+Ӫg!;d+x!JC -P2NHً9zAEMRB+ʐBg^H;񣈩Iν}, 6xղ27K<~6F a%u{g~#*Imܐ?Į+־HKnu~1BQce@0LSؑGBN#Ny+LB(,Qu᫱B3yz/A98[( _%(VeVvϥa _4a=V jdC\1ל (PR d}%5%0y4q~1rBAmݱOYmIa2\؝@@um't@[# Sc<ؤ#s_hd{Q4oϵו2BTKµ2;_l"M7QPo__Z~"M<< \ &JR~ae%;τڔKs%pI^:" 2H9;QqWHs'O}: !X+M2k{fxt9,aDq`ٯ H _3wi1)cOɞfTíƱYVDQ4ށR1 :[md k'.,s&v*e#RXzGS}Zq!wt7XeLPcTM6%}q  \vVE}@=QUҌd_jtRځ Z$ iú23qcCWZ?yL`q~mQc`F##Ĥ LSh_2@m?Gij_(1)\Y1^ .|طK]iC 1=B-Aڧ[['=YS|$VoҎ)Dzk [A^M(Ea!c5u%-ѵvT+6q%yybou sP7hSC/9){&|X I,b;:{UG@rZ>?K0gZ'Eu?^ן8a7,kqKDYBIݡSࡶT/ތH9ϡ?Ǫ8Kf.\rVŦx9vgF| Mg] +'Y+}_X7 KḴkwF*4r>PFܶf7ݦ4Ve7&D!Wݛa%wo{[4ZҐz1NZ8& Zmߴ;2=OzCxzmF:"M_0DIa.F; 7cj(о-r]JDX9^ܕ^jq~AKgvX__gn! ^hl?nD)jouW7a؇Ā)WXigFRWz_n2Ȧ3EIߔ,U|3=֩*P![ H<"y-%;=̅bC,ia|g Z %F1 H0} lO`Ѭk[B8,iv:R v7T jTIEDC z?6B#UϱbWi!qF { ʡ,۴E{q)p߬ N$kQ>60~_&lʮ;*M >dkJQ"%q1)0>۫>NԯPbK*\ %r;v¹DsubҲ#q Rcy-FEoY&!vC0 Ɨ͒$/7E4=a]i5a?I]~׾T)vQ5H"ukj(z. we讹eɋ`\10!Ԗ|4.TW_(-`X~Qi}a-dY%|d6&C؋di:#fsFkBIVbN O8r\ڨWs C`6IGR%w:"oW#d ^^'BWq?`ߒg `ަQPUnm %a*ΫBǛ0ٴZJ?k*[ԣû r UHzeWLfqd]Qa=g"b(;+ܮe(9`#8V\-*$h^1R9*o w.GHlFZˬ蕌j fygɁE a"H׏V[1[pd2f<Zuq턕nbw#enA❽3M496WttExwjkՅpN\r'3%)ɗK!oɼb&om͋.ҏIq"XMRu#Ȕ,_?{M)Un@|Pi ŭxj} 19җeY*r;4jW.k#{2]DjS`i8 PƱXWwC{iCP4dvj<;E"j͹3e-.(8` s/k1jT\ޏB'(g9@1,b&GyX6y4zNA9ACkhqzֈׁ4nzT}M#&Fm؆.טa~v?C Z^51lJF3265jE1XToƲZwL6S#ʣ-_!\ ĩΰ:=ڭsipK) ώSa;߀'X\XAaw% <֙ڵK%솊gs.!DlXȔMBY/8:_~-d!KS3H6j!htI2>!vx㑲z6(Mj3;8}z5~k04kp*")};7TqPi*1%Myߟ.J, vM1TWRE MPh8Mgwa/bUhn"?κu=|}:RA(|[-B0O=@umx>ޣA *US**"YcАGX0U@n.:6 IV-nOuC}6V.#tYu Xr߻Pi'A̜JU_sL"j}ڿ]RV\l"fb//~*k-Pé3*c gǚ}%ӽf8Q"]pRؐwsAmN рlgp!yǿ2~ ulː7r( t %a+wSW ޏIݧɽۼU wЪ.צtvT$0yYOHvh[epC 2ds(7plZj+%;[LۂghW#(ې'Ai T,E2JYg,=Qs>3f5J,MC۱C.N{}A~}niCsc;1S9[ C}79rt|}+_’VK*! @Fjύ 빚qx|jM6 X Q8B. - |-gr3!v41"l3;1~p9~c&r_җ՚A2]ޡnXgUumY&O / RN{zSng=}} Ft."{eJڌ}/ = ]*Ci8nzKA s9J+5?|:iP$  ,ES|"Ƌռ{#‘&]Ϫn(j).扑U|5:!n ʋx $v:eV  ,C.ͫ#֎ hq"4W:?+']QQ{MY)I $Sg@Of%{Mdu)Ϥ#] T̗QLWrDWoreTGjaYK;FEyE&Uy .zvGFYo4s$}fS]nhyDjXS)]_ [y!cB?蓒O1HrbvYP#~+.HN ߝUl'@*3Cy7kqʠ2RTO0x#~8r`Ì 4`}ٖ>H9XKA/'ZZ9No0J ,%\5#OtaQ-U+oVuBphy2EtU wBc$Y hB =BOpjqhAL%Ӓj Vɮa&ufD,wx۬pI8͙ 1!zoݼ.2RFY{l%#˫oт@NfdQxBfmqNj0WlXxݗ'OCۥ98Q;I n/忞ѕmg͑_!"? X͓;awa]v_tU96۾y8~ab-kHc0gQٜ;165Wy;*f~;Xg_',ˈ-֧5"S/S#9oƱD?8 BͧO;#E~+ƞGFUbݷ4k׏Ih|rMGXE*5 uTx|!@ih+e <։儋5s7%Ri.Lɼ^-|Vw-:k`e.cơj@*ALx_#&#ϪVɠZ\tU)U+0*1LRԐ̃*!uqY~/uQ7f>eo|G{⣸razB6t\ l|1Ў8IX͠7╁3jv2 S(r"L'ϒJ^~Qu2ޣ۲Pmd"αE%Bsω[r;FQ%1z\3C~>sԷU Z@=35`/X<ݖsTfTu/E?αѡ Z9C=65H>0 qeWNÜhq!'S[!vgix0 r/ ^kOQBXDʹdHV. *c0N m)7M߇,"P4Ixmq+mt;rYny r14q>NI3kD))(=~MfCK %R z)fmR%NSu:CՋM 6aLѼU3>׹{ Z_lRFՆL32Ga"2 i_K͟)҂}Y 0[1jTo7'=KenD]Cxܛ3-^UiRJpؼiqoE]Գȟl7xfˎ S ÐnjΗ0Zyr$ Б0䦱 iGn|/0?2 )|⩕IJ{˗Jh'Zj,2@Qg eg˶g[~bI٩Qi2>/l:۝x.o}bK: Ucc Uw!NCrW!%m#myc4lғ7k+C/%+nYOUlasTm\6Wԍ/t/s?12Q*IW-V h %)d#c4r c!79q'+kF$5mAi 쑣&704UT01 K4 \b3֋cP4aA?' uI!oKvZ6 Dbۅ8Խv=,E۟7keR*P3r@Gɏ?'ҡET2?];޷/|S/f>NRBN/TT_p XƛEqW K:B X%~i, qib,70\sɣciga>*v2E0eۅ=JBgxf 2 |[Ǿ>bzE3A 5BgSwDY-X!KS76v(_sJz=ХEң̀yΏނ'ðxamCqPԉT"M$=@R}nsݚb(BS/ 2{&Kb-JW3 e[J,T8FUi7gv@&oזgOgR5S~\7Eזcott XcbfYz(3VeWY"*kVI[7{1VXnuUug]T̠h[q"os%@ VWJtb_X>g)M=>+ Ҕ<ѣA)|V+uw{(G[ބOЯNW q >@k6;$qi|wrRA~uW>"#Z7W2(^b[n:)%ib[Z>ZTRRٶLeU8\E`H5 ZG%h+wYDL"0N@{WԒc`3Mڡ/Z>jIS, adK!2[/lmBxU-h+}blUqdS+3#j&94s<7%ȝK.N 2BРrzI璟(Ai&+\fcev4ˎ!ohV)/)U7K2G oyx M\o&Vs?Tv=e=2_0y|gHxfE][0믛sv5[=i߽HF/f\rnclɴx`8c>890H00o@ fj3(Hgx`pGwJ^>+;cJ"=}@>>4Msia]'EZs*57ӹ[{"} ISe?qB0TC x:}B3͂x_s6B%Ʒ_d^ኍD~#‹qԮE:鳉Qa,ۜYCX$q xiXz&6& L]Ӣ}05-!D \1ԋOgzxtP^hHL5Ki&6Xl,y- 4^tbl@ޯqW8 4*GA#BU;Tܕp/?h(]3l0FH7`B5 DRYxBH+?AV[/L^^*|rs?AC&X,mT`n^gxWcvkҽAMJORC띶Ggu5ISc|š8 ?uZ8 R/g6NzFFy]YZnfk) hEIڑ#8l| [p(aDIfū^sqJ54끩'Eqr+0x;S {YXmfó#+`_E\V<';4lϴ;dgY@ qjN*іnhd3|xp]|Xk}VP>4JpNu椓ʊ> 0Xm9 zw钊307/F{vU2 Ql('i#SPjg?%SZݫx#5s+=qpR3sTϲ ~yA ;~-a6Xk .e#Z(,`?cHyB #bP=W4]]PKE%9+U[NWcIoWP[pz3`HW3/(\$;WcٱՆ0nqd cEgvbkh(UsNpb1c'P#qoaF-v mAsL[7tJA [H &8TCK?+-UOCǻ7g}|5~pHݟ%#*%rMlF}vG9 ~[2RnLؚ Y,FQ3QN#@铊Iά#cSq,5VwrGYK[qypm|$މKOB<1)Ȭm!aӛ&\VJvBM9W r9D)oѤg?#Ar? +6t5"ZM3vPs]> ֗P#ЮܤB.Bs8T]rM2=H/y] B){Sg#{ QO?>p"K3 3H8"M3/yT7bͯ7YcΈ]@!ao! x6Cs"N dP30ϝey bڄW|3ȞVɡ~e-cOWOn Q!?IjǰPzX1uݩFX G.v/Q k-k{6]Jg[M(HI^y^v3zA ?ؓʈ6x(|HH[˄̞]6C15Qj[qehX+sUtp,7vk6$t?x4 K?9&M&CMSBo`px6* K(2XT\kgZRBުA{l[uɩ,s>遼nh5UW.8i_XZIc$&Y*?mĜW-mɉs[oy,L%Tծ,*!g&uz!ศLM^F@'Y}8ϯHML|`M?z[M&#'2 qfrJ0!&<}2\kz N0+3 2Fw݉s#totζZ9ORD$b"gzP!wB"_cxw{yn;X]?B4l' ۍeD":|b3,W˓$rDQoI| HtS1)~ՈD*qcs'V% _l997/wy#tN#;1R}QnʂNIHnaڛQI%dqLDEֽrsɆаy/`ڀ6r]GA{ܕ$]5i^Ta&R0Q2bnx.7tϕ'"Y4'af|("X-ZKS|RO_51{_U*XIT)HUxݑ M1AJ~ $etMrU 3Z;0B.1M)D7 ˛~_B5 ob10+ifHs*]Цp';J$I;P>15fbY9VUAa3>y=&fE2wr!W:-R1pTSشP)!N&ڇ[}6MߒC|м&%u^×W+n_L>RH'#\k6Ek{K9 MGT# ab; QV93+o j&\%X:S O` RFlЫ7cKdT`+G˴fH3şH-Mr~~BVܮ%x6i[2V&/4t6sT{PwFZi3nx&KvEuM!}jhLm78u}mnc$ġ!H?`MJd, "ՌVYKWG Pl 4sM}BLnX'`Q ZmG\Jz!bE}¤\҆q7;0Wďm$m,Dhg⍀FWvA6GiFEMt&Rip, -׫#Z''@Ĩ$_@B=%'֬5u,ž9,߇1AΑ_qUꍔg<~Dx?-~~膄d]f <h!uyP'6olٵˆw=rQDONtG%傆XVtd08zQoo SENB*F`$-OBp6F2sٍKE)FI!F] nKyPZX j;mNJ4i 탈ͫ@8Gc˫p_}[lżuqc`BnXфFp/:9C {32QVGB}ԆJfG"-!W$' BP]_SE'F*F\R3~^ꥴ]vSc [X (h"l m(ݹ[nֳl=W~r a*FNI4Í*&wp|yL-0'oZv@\VʓtaA(Q?W$J?p6ۣ:; 5 Ylv3oG}UzMg4- pG{P1բieǪA[SQ_uOqyyԬվ྄BNƕLIaǞc[W_zb[K_wHnϥZCK4K ~˵̶Zu^fQJDŽ0/0`*F+PjN;Ajh9/#NX, &{"!J.<2@.eP^Tow-$ uw_zXa$DsB ciQ2qJ꺟,s=jN0!'|צ͊)6fv )Om̓:Eۨ~^ף̻^0 ,G!QI+S.`ǰx ^s=ި d]ݥm8CǢJoj6+Z DrW\K_+}X#çD)֑;c:rZQG2d`6L6SLG-IY706v(kߨ#:c_jͭs'#iQީא8~â |3$o@{iX'tu¦;z4Un~!?MIım2x6uqq@K8r{ *倩1e&M(P#'&gend$=6SvP1/B ]e&k8R۞ g)lԧoCdgE{Y}y ۚDzNi8;/](.@P=0^&?Uu9[q;hhbWES4b(fL`$Uc>iOq˩ENvy:g9힭&QMP 5}e^׃ # A8" 傤%Ћ'TnoN`$0զ._`Qdw*.NYchMg&4T9X#rs%=P>Y^z7]l8 t$VJv9 n,sEpM]rR|" \j0\vJue ˹-?p]%gu7we;Q3F=rսzvx/%/:G Ԍ{5 gGZʙi.nЧ㳗x)ݵ]aL($3e 넙Whj1w'gR*h87m~q($`K_; O&xBe~^ =NK}:٩)$(6=T+qNx@>,|Stm\C.9Tޮw݆W({;)gb>W-{pk2(0Es,Մe<.C}(ֈ3uB+fq W!Fң)N-G *hYؗ$Z FArߙ3Z1C;-dm]ͫtdƨ) 0).T" L!p~{]`G~GgR󞒳J]jn炒JsnY4H`OW` MӦƖ,Jtpn"ZyJlW[pTzBA6mwg /{0Yb& ]V"2 endstream endobj 119 0 obj << /Length 844 /Filter /FlateDecode >> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qDŽ r Gէn8A{,쏘LEvDB``B9zK~;_q`>Wgy o.>ݫﭯAbZ%?6G_Nzy;9ڰoiܰ^]0zu\~3ݍܥ: ل0%1 " 0Z{q́0R0r0QK5<T`,if,1gT Hӆp1X:,p8}u 8alSM3?r>x\i"EܰpJMkl4\?ǚc:#?^YHwuprQF^odž1BЖEQ?1^׆ƨАԗ039+ãbLi~jЙ}s~zrCOe fYJ|֟uМ8gΈrY׆}ŊϘъ1LҊkgigϘ݊og3f3|3ߊY[3 =L3f/gd ,' f)Rx jb&'W *.MGZN(:p~7a?}]TyԟE}Ư%Vu'e% endstream endobj 120 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3FXҝA5(O)suߖcHQIܮQW Lڮ9ˊ6nK5NoغWi~r<d(Vu;_=85vѩֆu5CNmm悥+U=#)\][|, MHS"#p #>y| #:##0)%T\`YQqJƚ`ci|1Mَbo4m `2WQ/cW888sέ-./qJ;&\ k(d?F#h0\?Ipa]~9Vk?q1Bx.BzҬÀhƘ'g 2xk=6u2,bق6E0F,eL燆LY` YecODV3Μ蛳;zr֟P.O0{S3ux9(uF: }6,V|ƌV|gegV|F_+>O+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z endstream endobj 122 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z6 endstream endobj 123 0 obj << /Length 843 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N7R!ݪ70W?g_,ɝиYs{ ]7;׺v=ߩǡoݨM'opiT}IAu~\3;he?<{Q%(SVk-#&9sQ擾ݾk^!00j(+m$?Gwt>X.oTuþ{S_tpСtZ|I1?H/'BZV;ݛ ZԲW/{FR^ww?U4H6!L@@B@q\s *G|F/+>㹴3Z~Z83f3[:٭ ߬Lg3t33 ~!>CO!>S 33>IY ?BXIAup*Çq G潪N$p|eO_:q;:'dE_kCa endstream endobj 124 0 obj << /Length 845 /Filter /FlateDecode >> stream xuUMo@+H.ȲrhQի C}͌6jo73o{q3fѭVO4cpuU sk/wOwquy_t}??p]AAu~\33cA}P>>%t;en>r8`S0Aj~vUk&Yos yv rOiHM0[7v,ܜǽJnkz~lNͿvt*amкq۸qۿ`J-ztH]{O|, MHS"#p #>y| #:##0)%T\`YQqJƚ`c2U{;5Ҵ!\,18"\aD E_sN[sS9)9^W$js7 GZ ׏p$uX}/S/w"': fyRy(#c^g!ch"ƨ-kC^d cRx~h K^| МQV14Nd5cY9Y?C9돡'g ?%>O:ShYggΈrYgDg>[bghX|&^V|{ig33qgng3tZ[Yog,g-g B|B|\3gg3?f)O5[TT+&GUP#a#7q/c?z~#袳rdbP)n endstream endobj 125 0 obj << /Length 700 /Filter /FlateDecode >> stream xuTMo0+J!m0U !mTto4j{zv|tv ںQf|6'op݅uM{}ugfci"Amƃ}>,%rtPRJ(:X'Ab~oںT7h uSӌ]Acq`sy̟M.n? D`އщ7+d~4Wj7vw VRŪ,ׁk/bxO0+,F )1!Pp #]QxQTv)#ZBYLt/X^r<1u%pr_d9٢PSi0@WQ_Uh֩h諵"qFM]RrCpt39Âж~j3Fezp888Q:1bc7~}Hq('bĄ>^m# &zd}4)` "H,4%!%AQ߄B[B~)ҙ́ _)M?DM;豬;kyoQnNRd\Ӎ;WA} zoZZgbT$Z|U endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 811 /Length 3430 /Filter /FlateDecode >> stream x[ks8_l T9v'tu#؜DDe{.@Qlrڞ܋ `˙bJ0̈́q0$LKP`)hg1ہkTd2gR`<+'wJBIBnBY; aG{szTLXRPgaZ/^;"8Etc%Әx04`1eidaP~3r@q$ WĐT5L)Mp&ORSR\Ojo^׌ KgUsw4U9)͜r^/fr+/GUesT />C?c;NkhyJ㙘ژF v1X=e/Y)S~^!6wy>Ӵ<ޑ@m0dU=K& CL¶J֥t@>\d *$ֺ2 Cu.hB(X?LQyb<"D-yR:w@? |i,R*d<(%m&*C--ug@h_m=m;h/5m¦meQKyD*1=oJ b3f3l#6 GJnxVxڇ/~&n?j!a1e|Vz>I֛TiTʂIє_e.v}imhkyՌrƞϊj8g͢93-ZOduLb_jżoDzŀ3֣jzE'ބШxUS荴O:jz4WÇ S`.pEݔCK^ Mɇl|ƣOᬞY t)C* 1<ö6*?Cxx\t׋U1[LŢU=-Âoa|Vy.I'N,%o̦t~+_9271.%K ꯽Ž,R[Ns{(i^G51<ٸ/=H(fڧӉfV}I,w~`l<|EO~Q7RRP2䆒1%%%:Q'7BkR2CyWtcU'US[wR9% J.JqӵnĥXef>ȧ޻Nj"ź1 xݰC<^/;ZY u=w1xXmʰ{6jRjr$d5r6墭gMIb-J<'{:ܝT;e'L2Z瘑5$\SVdAAض}[RO ]nxCRhKMާ i;S^DK`+:ZSu$TTЙ^ך&)#r#JrS].|Ij(i3Jd2u B\|EoXyȹܑJ!xR˗~-(V%Z2JfQ௹=e\c/luѝnrۮ`olMIm+"HVt9kVq± XIlʣ f<$DSdTC)4M)%$fs`NJ*I2ډV88,j<]oN8B;fL0կ1LfH5x"չHydZNR}-?NuF|# ~1oMI6/6ᬺiYzJ^=ߟՓb*d"ٸ3$C d e/do-Ҁv(JM1GӫqӔwހ޶B6t\3z{7C>%x|§7RAH 9oO4gUӗ??ʫŸmFЂ=n>3| )ω}N"+tk=_3]l@հSI*Bk~溜_;NiכrV#[zaz^~yu ֛YY_5l/g!c '=m)I; CX֝4lsgXy1 6qNzx ={O=zrSO8)O>=jFٶ}OOr7 'r_Iu} ~s0}=\FCE_їZ_Jy yDZp&ƴ-[cZDV'Mz#:wp13\uc>?>9zÊCKgϧP=?~#vm_v\ZduL.`WY3S/v}V&ٺM;~#?(}38#Uɱ1+|禖p`k|3 ]r 96v=j{1)m}n?)oşUyC^Ԁ(ڂ 5ej.=B~;PGN sZon+Z0Z?yD|p=fhw앛h&Zrk@+v ޡVo+v린Sf 谆T]a@}Tp;xinNu :uwoyj4_~~~~~̑!/v(۔{PWMW7=^-q5N1=B'gF \my=+?}RWuxIW-u=bLJzsJz}ޯ'u]tONᛦ5,='8d‥G[(^Yٴe}R8gOxnC*ᨚ7I̪ќE5 ${؜JyS͸<Mqd;jfe1>/.M,ZǠɢ).q|:`Uٓc$"Aҽ2PWlmR0cT endstream endobj 136 0 obj << /Producer (pdfTeX-1.40.24) /Author(\376\377\000N\000a\000m\000i\000t\000a\000\040\000G\000u\000p\000t\000a\000\040\000\046\000\040\000J\000u\000l\000i\000a\000n\000\040\000Q\000.\000\040\000Z\000h\000o\000u)/Title(\376\377\000S\000h\000a\000z\000a\000m\000:\000\040\000I\000n\000f\000e\000r\000r\000i\000n\000g\000\040\000S\000H\000M\000\040\000t\000a\000r\000g\000e\000t\000i\000n\000g\000\040\000m\000o\000d\000e\000l\000s)/Subject()/Creator(\376\377\000L\000a\000T\000e\000X\000\040\000v\000i\000a\000\040\000p\000a\000n\000d\000o\000c)/Keywords() /CreationDate (D:20231002185750+02'00') /ModDate (D:20231002185750+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/CVE-2023-32700 patched) kpathsea version 6.3.4) >> endobj 131 0 obj << /Type /ObjStm /N 5 /First 38 /Length 268 /Filter /FlateDecode >> stream xڍQj0+XY1@In}ҦH#X)_ߵWғ>!4W*2zq:DjZ1dj3%.RiFWq!g8LFٶ]H2N=n:/۳Wk׿,~V>m6șS6eTG8$q<7CymI&@^aA=DodŞdåg"w_gdⅶE5ʟ糫l|dƍ gpTL;?<) endstream endobj 137 0 obj << /Type /XRef /Index [0 138] /Size 138 /W [1 3 1] /Root 135 0 R /Info 136 0 R /ID [<108ED8F1861E470A5FED3B3896BBAE03> <108ED8F1861E470A5FED3B3896BBAE03>] /Length 369 /Filter /FlateDecode >> stream x=OasP HhDQjՊHyPPڅ/<88`d\&&6FW9w刈y"?YH 8"Q[0` Bz8B6` TjTI-*84Y=I_C0)HYTwloUV/y#p.g_Rw}e7 W! 9So{㐇\*m:T-%xw`&.)(s؆YX9x`CCwAҢKU_--H/-=V?wה{(TŶrL叶 [Ѐ#pL00Mky6> endstream endobj startxref 218673 %%EOF shazam/inst/doc/Shmulate-Vignette.pdf0000644000176200001440000053450714506573206017345 0ustar liggesusers%PDF-1.5 % 7 0 obj << /Length 2336 /Filter /FlateDecode >> stream xY[oF~ϯ/`MK&6P[yfQmK*I]9sl>,z8ssϮ7o/U4a&B0lmwapo2 E| }V2| U3!Xbpδ-Tx?<-36 PRAr/+x&4SXH_hx$hEUΕڌG@Kx"B aHy-ȣ;aHyIS)u@wO3ɍN?hנ/ eB1?_Yk KwN!}{C\əzRpd223D,la㮮`f_uB0h? <˕Q2xjoP d/lM7gЏeַAF'riȾYAqq:L\|>VI[֪]Pi⌴.-פDvhSXK}EHx@.D.QݧhHա;L \xD|$ "JRIsDL I1e>۴~̜ZAJߪίnXNh}zyP@{Ak+@2ZNn| g=)jr<ɦ,C%{ctmec΀mtWvվ!ŭ/ޗ1Ai;bH]G>|< !ґZxOqcp6 ,僥(XF3ķu;s33 m,rK됌 }ѧ?aClWl`EsfEga*@iPCǓ$@X$s_2 27>b"#mT}QpoTdp+PtE&~+B`>A2cc֑|U:*a:3% "hY ,bdPT[D@8Tc]PBݶCc7A{?rAׇȦ KSKl\!$M 4m `SnC= *۵=#^6)%7bQuJK@sL$Q^µ7e="{C6Dv+THS6`k:eY I ~LZ4')ACmO1B&H@e%07J~R>,U @/vEW@&՛FGGh;'+g^.)ToԹ$ܾZ0< ;`HwĒH۸UBMٿ-0qR* <_^]')'AL8R+$\'7S$ 0D*z6N%i^T3>@X`_ekIY`!fN)3۪i zK3i5\Pv>zJ󣱗)BZڀN5VZeghvt*̕|S>do!8LBC 9 tBa$w7֬Q!6^ԭR&wb}bI_@F1ky$  u˼3 *]k", akHӡBG9yL$F[b^(K3FGݸW5Y%̚Vwk+3R>vI5 ;E q%lmTLs"Q${F(}\gmS9t%a'pSx?g󴩊U$1$>EթQb#׾S$cE1)0<-}tE##kaijH{ZUtS:!cB`p@[rJSaf1篩/u`e耕W-9eJƖI1A񄣶xJxB‰K(k=zYcq g;CU|88mٺG| Gn&M˚\ %?nwU7n^`?Ǭ ]nhw7rPuZ?^Э7*ˈq,K2GwGrWz[  VȭAb4ݺF;:_^,/Z^-ϗs[% }_[TB-XYaw endstream endobj 24 0 obj << /Length 1780 /Filter /FlateDecode >> stream xXYoF~ ]Fi% z8F@kD)$]J\Z-P=كXXi - "V<9x8EZV@"pʭKÁzE1_ͲFlE7ŢT-cj2.ʉzmZF.nլQ''/c="&YO/r1;J_w,#.@}2,{q]5SZUO9!>Jc\uLxxF9Nʖik˞Ү:s%hBwաLr!.ׁґWY4II<&i_b;4:t)yMO>cz<0=d7 _Ϥ1 ]SJC(Bԏ`( GOKw# Y:\N|<z?8SwtS˾جI |1v)/{qC0y*r\!PښT,&; |YQpj㨉PCJx'`⟮3n w"H ܡYt'Gh$]F}c0 ajUY``SV\sR; FDw4 mmD@GilH1@wK1ldjhdYc tw%ƢD5lϥ]8n4;=ǡXrQJfB5>0j u-FNLu7 XԪڋzYj=pJjFJv,#+TsPyA@$Ri}h5^TrQsӎс)lָD d*cS w]p>pN7{jl>tVm%jQLOBK!*[N%sk7tuf9ݱjN\`lO1n`< UvCqcV@Uܡ6Pu1^vV,jVRKwPB֛ͲO|g=헙t'POLJ!1Q\4Y1@>].ԹCz4նh";`隦r{~H7tH̿I)D )`;.sŮQ1j.Kᚙ}eX 2[ YB±[dmUagMS׫rBWXaaӪ3wJ5HQo^qAND5[-6p i؎|ʝZ˴/1,ʺnb"'b6=Cbd|~8sۺ͗JpSTH$|x=+ݶnSc-}ZCZL-yh"!_rKlÜ' O{uG #J[Cp>uzֲ}G~֋>KTC-֮՗Zp4{uļ-;nrq[hOmNg}z>ͥs} Lvi>?pD$YDu#|_(!?\睫CL>bE2/&Ӵ^-Qy#ГS%&pir]JIb%` {X.,fbwpXq+c.gBqo-Ph~]fy},vqƊcopؚL],ob'm.LLX|x}H݅XףX]4*m$]F7`րYjbކ endstream endobj 30 0 obj << /Length 1540 /Filter /FlateDecode >> stream xXYo6~>XK:,l(EH-TWs9C[R\Ca" ͭŭgYgk`45n9VZ!`gx*|nS&% 6ɷ) YqVQfL\x2EiYi)[[Y^U/\ᮬ\ΪlTFAL>k2b >i#cew̋\;+u0J%xnQTOlɊ1ުhi(AUO=O؋̷+ڂ`,;lg L SV!Y7#fy e&w;^c\,c|Kde%qKNݬ3򞰍3E eYq)dl]y(Bs\뻆bq߹WuY9/7X8}ePe:Txo-sy=ĠR |BS=gPfSc qCUn 2%j=M=}s {븼 D⫬7@DUONL2 IUWyפT7RS+>_  6 ELd-dXX: 3U-9N2`J̈qi{ v,dG h}+j!%pYxXS8+BI_SʛtLͱa繤Ik2KOsKcJ9ТG=O#ڣN7쩆{칾Ra^Or7:Դsyas=>y~CM+2K(H7(MIvrzw?'xY-t6KYuy:g~S^gO֕嵯5#A|`g9 OΜ?!zʁlej۾*De\*ӕdpnX ;'{J,/s}Z: !{J^#`6YXџy YS08X%yTE,'Oț]C҉/񁽅4eA8>ɩ<+!>EJ[ڼIJΛp˙SdХMU"{5c*9 #Al/+= +9Ňkwf]ls5*:|ebyAxBvʢU~9/'A?.LX(m`ETӬic5s nLrt endstream endobj 27 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Shmulate-Vignette_files/figure-latex/unnamed-chunk-3-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 32 0 R /BBox [0 0 540 360] /Resources << /ProcSet [ /PDF /Text ] /Font << /F7 33 0 R>> /ExtGState << >>/ColorSpace << /sRGB 34 0 R >>>> /Length 1049 /Filter /FlateDecode >> stream xVKo7Ѿ_{]Ǐ FS)H 'GjDj;/RQHjp8 'Q|JX=DY^/;=O~s6S[>7W;}6>L+yKKp?LɷzpV. B.1KvL\(X" 4*Җkr_2[-ӂxX&V`YEQTr>N\3^h]NfևHY| ;Kn4]o%6dUʫ)Ő_wb^LYu F]d)mDXYm"Tu5. TjU6/` vevit4ZMj- Y5'íRm$ Zlch$2Sf2Sm$yvY.mF1&j۰ͪ9n2o[OײH##|}VMf !on/zxg~_xG&wBЌz4-:6-~^?~AF&pL#ͻS33(ӧv5M(wGWU֟┻?n{ŝ endstream endobj 36 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 40 0 obj << /Length 1428 /Filter /FlateDecode >> stream xXr6}Wh Xn$?(J8dZNa-/ Iw-ACG.=g Pta<} 'CFID#6L!4"TS6{x41lW~eoi2K +$NI2iXih4RL^1ė],g;XF`6XpT{V[p+>nWp KXW߇oڥ+>`B.'mW,v̬ .~}}Ј`=})vX2B9!p ah,$۴hkFczuWBQg,Pozī.@q5R_ee5fhݥzQBtKJTYvB ѮZ2DQe- }s93pw*n]aйnҹe50<3OCu`>b^.V}^jzi=ehonB >m,F~$$hd:Ė-Zz7y[e"-df$;=*_,}M©,֥2,+Ic^!mm n@|w3&NO5#Bk0gXLu:}9#4&OB3H˓~!"iVت/i*e:L֦CS8q+!@LVpWd!)L"jLFly[氠ғa*N= ;R#ߘ1."}[65T5EK%}UM@JȆX?@Z ;ɉK͆sJm,$TDf65=NKQ5=mӆHnrK?7Mmzݤ@s<;a-͋b7~Lz]eO=BW>,6gOI <@IFi,훅^# !ā%"_Ѱ+Qa0.W F$:tEuU!}K+;ռBL)[ϭr>TyE\K%ҚHN6mGu"hPH.2m ͏s+)Q#AtW=V/5Y+fFI/(yv}ܹ۷&]-V]~BJO]ԽlH4-)!~2t )+U0$ߋd6܏p^>$B3陁~<(y=>~[M-t*4S׻zb}E?*}r]r_o5gHaIs]zXY#ќrLٗd̢ȧ[ka]sʻ_sw2× XGY}gK endstream endobj 37 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Shmulate-Vignette_files/figure-latex/unnamed-chunk-4-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 42 0 R /BBox [0 0 540 360] /Resources << /ProcSet [ /PDF /Text ] /Font << /F7 43 0 R>> /ExtGState << >>/ColorSpace << /sRGB 44 0 R >>>> /Length 1049 /Filter /FlateDecode >> stream xVKo7Ѿ_{]Ǐ FS)H 'GjDj;/RQHjp8 'Q|JX=DY^/;=O~s6S[>7W;}6>L+yKKp?LɷzpV. B.1KvL\(X" 4*Җkr_2[-ӂxX&V`YEQTr>N\3^h]NfևHY| ;Kn4]o%6dUʫ)Ő_wb^LYu F]d)mDXYm"Tu5. TjU6/` vevit4ZMj- Y5'íRm$ Zlch$2Sf2Sm$yvY.mF1&j۰ͪ9n2o[OײH##|}VMf !on/zxg~_xG&wBЌz4-:6-~^?~AF&pL#ͻS33(ӧv5M(wGWU֟┻?n{ŝ endstream endobj 46 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 49 0 obj << /Length 276 /Filter /FlateDecode >> stream xڕj0OA7FST ;K譴d+b122C7%|Ok[0Y^f\'{vHҜbBr@9"Q>GDxHܧΟ!*3-[zgdLZtl 8C?[8[>k` &ˬYR- ú|rXaZ&*>.~XAj`PXۗmXњñV:| RLZRS,{QLOEY endstream endobj 66 0 obj << /Length1 1846 /Length2 21220 /Length3 0 /Length 22397 /Filter /FlateDecode >> stream xڴyePZ.wwk57Mpw {\#g9s}-^kuW5%9( rpadeb+MXEAv6&vxJJ1'5A r(;:XXx)R@ӻ` P{:Y4&e `i}w9z:Y[ZO?ޢLY3[5 ˤP 4 )jj)U% e5Zj ELM]C ..j24d(y7 ., VOF^?Z8Jrqqefvwwgtuva9Y29U3d xw"N+I/;Nr-?1ep+_{kٻ3/hN1W'?9r4.]~2};o_ޘ߸c]ϝY;%SQPSgo<F;;L..Y'". fXޛT\ do^3ĭyr9y2m@ZX;[aՑY+PFlEY],'̊O:变_oG#km|v6q\\W7g[7]iR 59ý̊ f3c%jghboBiebomv0)pv+[Ye\Lޛ^~#4̑{þ/?; {/:wT `VPWQGe$`2vqpLL{S̙yU7hw Ή1yZHÏM<?[A͢?,a)@4ai_(޴vDs12<|VNxq X[}~@8]"qh#@(c|`%r$TMC^99V [wWב X!R&s(-nD24Qӡ+9+K,r<YbI&1$pljAҹ c}}yoeQu#6w%.r&7|3wb"՝rb 9hP౤6<,CRTCE'7!9(=gB-<ЯQO߿X 1줌" H}`/֧ז.CƱ@,y$G;䥨WT#GL{I]}V;]9RhsUN`n.:-wgt~4nnzD "lԩIL Vhsza'idBɖ&=Wðk(gj纣F9N#Eu6Cު_ۊ o HʿƏqɎw+*ry94RShfIXrݝwhf) "!⳾U-aP)3QXnTiC%oj};&%A!.ulDw$+lIa8f"u$+[k' [CX!egP<WaG4&2/Cr&'ϼ-OzYQYu XJ ql︿:4Adg Wa/TqEpߢm*bf?͚FԌ]ʏ oVWx;̠C<@A9zKʑ7Z~v#N颭LB@ mS [3ۏq?@e΋n|F,De\D&,pg7{dTfW>߾z5qR$%IF9=i' o*Djpe$Gd+;xEq04IKz]N*ٛ a/gTtgGdLZB_V"ąئ%ͭu>q^𠞙ԟJ`<;}ƵN{p.pB,cI"JFthUͩa% uv%gƬw.Q|z6jKa<6i)9l"k-׬l*̀ejͷ]ԼmaƤշd2&ي u1ᝤgIG!閿hP>8VwP)$ja On's+1א,\4C.IGѬ*t=DrL#>`Q`#I9A¸0IdEs MYRd\QFr t]51{cXŤ;P~Nݕ |CǨЊN/cduj}p1t!Ov׸ĊLj%LN4АYk|fRvz1ŠL5wfY=ZQ(MѺ(㷁 N qyOm>;8,wt*rInP>1VuڟR*M⌑afm*\؆Ww~dAR!эncBZa>t{{k]F%WI}^.o_T29z>jCL0#hTSф<1[ "),!rf[%{9O :YtGYF8^')U_mY\!UC{)BKJ`_T\ ;?* j| F/Zkb=mqze UFЄ1[_)ךA$J:i_3]wԽ>@ o7QߠvA" /:t^MǦ$UY2KLlC*B8x_)tSۻ |Gݐs6Bxp_<0_QHi& ŐoK ='OLM0Dէp2z g;8΄\ o~}6͜@loA0RqЮ2$y;+V<[gğ #cfnE˨kQT@a|WLri2- ʞ58Ia`4K‡iReF#kנ0%F')y_W0>x)-lY(׻^T)W':%G'Z_h?ZYkNf2#hkS hu_@⳰!4jA;S 1;W?3M"nfS5MFopfQ, uP555N5R$nE2Ҋ8N5SN"BD:!,@1C3 l&J%ޏX=q :RpT~>8џʫiוGbC8/c*'\lØ!)i ;Q~bT3̥#$um.wN.+c?O.3`_~pAauCXKAF![RY(Im#J0JR?~-ⓥ"@`@whB /J@j+{ ȿ ^KfxL(5C#+BfSY_z%?/ܽ6V'4L8"K}`*4^zA %vE2B^PaWz]ye EѴníAu7A ;3U䃩/ĝ8N;~%[~J[VN' ϖTpYgeD9eJt3dݿ&`ٲ;KW  [Ϋ0lx6ju5د5˳o JW( 3+f>E s142$06cyy:!Zӎ:ӎ>J¨[U }A lcȾ[=)/9[&YJ6zryKm#5F:UwI-tzt\$՗ |ޗG$ⲑ%0`f3n^ 0eJ1 be.JN=ۣgrɝrxQŜB+2`w>G$d\]bj3[m *y|| n@QeQΔLCT]yq5#5"hhAuH8eΡK^ zcQә#3.-mliҡLفU^w}tnl&ɭ{1']TxVD  i:B\LĊ ?U#Ic yiBqcM%L H N9{{KK\`x:T, +mֶ.xku~ 0x̒y3B'pQwg339^mh]$`Z=^FloL_:MIHvx;7t+'XV6 3YO/3,ȯ+N`e.ǗsvvxѤD ~n!>yݑ/c)mUWi,!ǀ Hpr4W2WLX+(dm U8oOzLO: E4=Fexq8c5A"t$9S>kbp(-(vi "ITms.!^f (li.A͆EAQdu]^D\D *[]8yt/ΟiDۡ uE핓)g#4]2kS)V{`J7縌+`s.K:] f7c6C`I(GXpr(+A'ݫ"~_ #N$ T[o{ 54_l{WB97 N$.ss5U>J!۷&1xi?3|2ط[4I^[kst䔘v{͚2,[fuB\)ZZNG:eF=)(^:'RqCksnk.斧͗Mr{;ɾ} L5S`ڸDX9 5983-YNMW#),ףW2eU#1x`ttuHG'vVXZ3HIu_MCdHru&HٝkRSuZQ/Q=FkO)9qPØ[ D.&[B>IHhٜMo˭*>TgjqZ\ϟ89[hxGk]LNόLfg>x㖉wq5fCu6~ tј8_x(s5I{l |ZĖBdI*_ ,y?켓܈ayss @ܾ;x㦿S5c`)GY>r|M|U8LnGa ̣)] Q$o^":auɕж]hO{ȌӢͥŹNZf0'1|?5Ip)QN[d詒TK2l-택7!bU}Mԝ/nb'?iMrobvP%=ՕR\_QT=K^>B{˗K/ >Aiҗ!8jY 1 MûʪYi0(} 5J 7CAnaH]48c R\jGx ?7`B*\pPڛ$_!C=ڡƧ،<^ɓָN+Vv+ ,-?3«2=;!VGE ]<^w+LHzRaE%e˲,eaOk5j;= +>0eףh4ƫ)ȡnU QsS[{5@-#ٗLOI[(Tm0013L~7F퀱ѭl'wgdnF}:+rݾ ނR4m%Zo١y);71^&c}懷kێ{vuP$f5S7o+Ȣ1 C95ȋoT_XQb((_{r=}!e& L6"qRX9Iʃ<֦ó%mb9'Ʀ*=}2Z[>َf&,#ƾ};ɫ.Z>êmS<˂؎oR#$?+ lU/?ajtAX#q; wg-w1X`C ɺt#yA2?)0m"LӁõR8GU[Gzw<|ќ~4gcW.f>dlmd*Q$)}QJL&vI9L\?~ BM(K%R&lAU]ӡvjĈToL۳5AC[[s ɝwG?IΩ! r6+N;a%>G@k_[- 1@qJy%T)h$CǕY6329RRkx<-e_7`W u"Ĉ5͏kB?ee"|~ 7oͯXLX{c䊊_#ژU v-.RֳQ֒-I7 j0aZM{m8 :Uևsx*OO8Oh`Ƣ: wTsA,3oNIgfEE[ٌf&%3E&}! k RtOjLАf{;b1.궠cƁ^ hr>O?9:]Ce?Z)m YK;J~k}?S%|p(gzpn4Y` R~6Jv7Գ(=1EXU  =me^O^^LwIA=^0G9|omh0^qdYSZZ(ۇep YHՒ \FA|%0}zONj[Kq?bk(=зkM)ܒ OvQZ\c`ӣ-e,fݵٶ}ʑo %ÙYC^)UJBuY^0?}ڢ-K;b,8L_MTq ˴\OFkdyޏѫA M)H#bdZhg 4͹{$o¬,;߁ u3ͼ  V]a[S|9H#ZS82ѣm\,8&^ób.S4-lڨrH^ȉrs+eR,%QPQ~Ama6rgE60aۑUo2 \2\Tx_+6Il5g<;r!}clM3;;]mv=-6?&T '2yɆ>ď1 .bR@/:/&5wEŬ״9>8Gֹ!G&pY⎺gĕXy$ĖK3G;a>RS˵ɏPn;ǭ73KV ֖Ú*Z“ZOu$1g^%H0Q*)\35ϳZg79\uTXJjJf皞g,֜$;v^\b)3G>Kk/hjֺ u=i?~a]^CI!w3tF0TN.kJ]{N]&ˢR -&umydl~寂nA7?c}d[BF:15PLF+Ը/1:~ U|wsl?ŹWL'eOU^Y [5 H;$H<։NuurD 58kCBI:.pKa+4H\ծ$ ]هl{{ d^?R@U%Tvpd wGT1Vd&FͯY\r+Ô2'Z%}-dD߾TRi.epSv&VwEl_T-D(5~eX^\ά8Ŀ!W@ Q j}AcKKۺXB@8cΆnR!<!r*@NXbl\io# \.%>|t׼Ql3g+RClro Gsbhj$gɠVc:y* }S;~ NO%8??ÁMu&ddwH++^ f9zGӇ[TȐ6T6"(k]`F艎Dtј)n@^94[NК<͊ @Z8g2 u2ai(﵂<6{ѯ{~ {Ikn…~ʋaV#X 9D7vSlޛj+\/c9蔽 cb#WB g/3;4ؠvJgw10F^Jy.6u1CeDBew2,v||if{t+r,T8&÷ߴ05! { wc1WY8[cOYC7^GaX7_78WK  N y*5ZPg e ETD<eA -F>R =@ @_3X:lqJVg 0&YNcw'>LjF3WS`0+E Igg=7w_߸' kD,$i8zSF˳Adwd6uLRㆍn+,qԒ| l2X2?%#vjx}C9qL>=VCa0hsHfNk" ֈs85ah~(<AަAxp+p~k2.tc+9@YRb[  Yr)֞._!&Z=m@hJOB !ڴV=ORw>-)Q& GG2աyU2vf٭C^C΁7EI FA\5 M2*:.EK;˗j@)ϒ]{]cqRsWPW*psS$G Bx83'ht*wSs! Kj@ɡ & )˻.äs%X-RrѠ^.hCEN&z V*ΏWf͵ؤ"T˹"tL0o(.jdFrP~l?7+R9۱^ mڎUiڶ=[<%אv,z&gbQw3ς>^(A$s%v]6f4fFW} ssU>yaO[Wj:C.ʭM,Ypʐ:٨QX-$U,`ËӺ`8NA#f3Y2!{аؑ*3u,*>gگ5kɢ6z*_ܟYIT#ύ I5>Trr(M}NN< vĔ와}rJHb.&.&7O{ƎS, #|JDpCܺSź3Ei(GTYFՄHFw<0esk.<;lP-9F/ C# 21|ȥ-MLV$GxgS<;Yie" *ҶOs$br.27aCriaea\::g>4CPٴh_rg8e o~VhJy9CBqM,<"%ERo"F6"LNo 4E'ފe8j5Y]*YzL4ǫ]5l"bkX JHOHѕ6꩹ Vno3Eq,'1'p/ݼ .EKJ 5]U4FĿ2NVDѝYibldhX({PN4N[(^S`DD~\BCE0!a|y.͇.f}dC#3sZ["!B$!h[`o3Y?Q=Jٟ7-k7%'T22A^ΔGhKMhFVX5v3 O1tNC:3@9; a{Q:ޫ {E;AB9iٮI7i|d[Peí"`bcRHPxi}g` uxdmAժuR7|C8<v3iiNJvDlw}t.PrS#!]Bl<ʰ۹^f7G QuA)E*W G>`Og@}4Ȏ'7l^  ?>Ȗ;h{4R/B}}0 GNnQw ֳ:R1^K˸F2@gY((<6q)%o8}L[2'ͦ9`Yb.q\?̢&M&ثMΣ+1V_^2$|v/ )[|m9 d^new9Tq׶A|'^7 }iAv#gAܰ#3ȽdlWw!$&?41-K N}%1kE3 K~¡zM2y_Nay"0|5QN؜t蛦=ݛ׀}1NQ`~|ϠC蒜Z'0Զ-EZ}?LrHG~ֲqYzETQ(OW#~hhT2mp w@b!a8߫l#Ț%N+Dpԝ21|QjM/{ z}b52|VN:Tץ:?H2ǣfgўڤn emߎZb=(~3v{hq5cƇJ6pU{tو&[sMTIeDϛ즯T~U 09_gůPOL5,AfY\?3K.9(@ŧ[u0 ۏןGWsإJ^ͦz$z?A;D¦~4zv^SJX;^Rw,Vn^ѝ?lD*J9*j5/{<5I&!)[v$om~ F'::ֶV x1fKڞ,z)}|[,ʧwUjs__uaůDF Nc7*-3Di hs:M,FDƖN%*޲^cƸS!*wӡe[ѫq /.=U)QUzFF-A9z21 olm|_R!Xԃc^3_7 1qݰv~| g=̋P܌-^;:7׏ChR?g { rN'$ESw!57`l{oYVQzd4.m)h3WOD/}׼?nZAMD<&E=:<lz5!"@~fsk"rL7L8XԈ}WK *R\,]10uΧ*AҐULXppL"nʣMe sFIf$=҂:d FFQņMwx3¯K24xk.c_ۄTy/}99Q6#C~maZnn21ܶM@؞]ye<+ p0:qq1>QX[G]~Gd4*D?"Q(fv @WhV'd+$O\NvЁJ):.3ms)AtAєt,8}j*52PhaJ=`כ}x?tLj=:Y&҂-D E@лElJ_ ݚ`0v}{!EيuQ?gVBh%qS^|mp.#JvŎ+B3"xftvmߛZsNs,%DjȴwLc.j;`_RGTik#KZi`I a㗌>Jr*)3uCBg{L0e-UâNE.QEDA|dfTŘ9,Yo6幰\Rr;2@O>cJF=G׽| JC)ɲE.d}Ko eOvR Oo, Q^L$Bɇ~&q> TMVB,Qxf-^h~Х,lljB  mf|<9)z& zO8eCE=FY hz7@(n\g$Ԓ;?|x5#&|t0N;=gVؽPͥuxn+o#@=jd޷/XO.S Κ(a@#Hs J]NR&Ed~jv61u`D;#V S}B4M;l pCIVB[nb40D7#D%4JMNS7LavZMF-GlIۘ"abt@tnSKGsU?w'ʵ+>$G.6 kNIx!Rï_0rqJA+f!=7cܠr~j$d(gDZ6سT-^FN?,t|%hL;4 #b1c-63 i3 $WT_di>ZmW?PPޤV t{_D]L)e5RjQO+M l[Cg9!T,`$ө8MVvEM/ `s*#SȔ`(`2]eY`|N쨔H4[bV~*\NF1+*~p :k8@0q9w_IZW'@q3+Kˑھϴm탑E#J_%R#DY !'Fhq5pkqBZ rI+?T,027Ҧ+oZ]`'g(kO- 뽦wdł5u̝Kf 1(ưw<u $e5hWYޗ y0Gy^h1zp"`v\Ÿ:0JưMl.y'.#sTex$dҾBw~y4 )=ZXӗsHsoZw>Qi1m(~օ],BvTpyޚOkR glCnL -[ݲQ.lJ?"I \|0\ ҒޜjVo qVlXk<#qv)6$Uv[#z^!Iy6~~Pa[95eU"p ™W`DNf9yuu0ݟOu(Q )3JUdt܉W2W亄IX[rf^rsB/I=)}cܕ,/_|ˎ/hGĝN >,k5 5l9coٖ1t>*Lq,M~gٺ":AVrk q.m!,]3Ӣ"9il 4eܺ[{3eU+֤KiXQ"̗3Gԑblq⶜ʫ-^Dx2ۧ HkGE;oRϓ--'Ա(MH] I F __R?R3D7ٺf yߠM6%%(ʪyhkأ A7 33&(FB#1#Rd{5tɌmbTwn*eĠhõ />4*uܥJqI^Se\4 (J \CIV{=?jz4aͲG4=,<- Kh&Vu-DĐL7Zmڈ>'?[Z:S_I/8eVCp(ܛ;8-;&ZZ޼h_^)7[P|c1GdVO1ْSѭq DŽ/.7w8[Qf̫+(~; |1S7P l9&{=ѣ 9A뎬 OϘ?n/uˮM0ߝ!hXm6-WX K^l# ΞO4.Z#j%000+ۥ}qjj*;LiP6o/7ˤ*O+)Ayɲt( -> ? 0P˾zR!пf&8YM/M#2<;\J:2sfٴ%Ǻ=1R/"8`r֠\RO5yi.͌i[OҚ#$8s=#x <rY7ɮىdnC˙D. TuKdtcON4g>F+=h؏ [Ly"(P|}i + IpW/ '?d[\d IOY7_Wp#[b&&-&\7h%0\E24gWO0QTu7ãӤcyG|KMFeF߻{ -hpy-@j(ߑJ^Ӂ@).Ҵ{w/o mS‡+Q))}_؁gp ^T" L -62A3qb0Ƨdl t]|=f7[%={_m__':aUjq@9e4#Q=3V5_Lt\$b |F*4sժT;cgvm޾2/@fixT{(UzK={)qK}K!qJºRWIlǺNo8ҷ Sc'c*%$R˒R/a_y;pn]X3kQCT+,GCO~]S˷)+I!.E[#I.i1C;{3$#M18J}NYАS~-x+*_fǚ`jq.j&7OQpOz0/Mf0"p \oC:v2wj/p!a endstream endobj 68 0 obj << /Length1 2470 /Length2 29794 /Length3 0 /Length 31237 /Filter /FlateDecode >> stream xڴeT\[5 ]CNݝ.-  xpw*vwn^{3FQ+1;@L,|EUG{VFU Rhvt0 +{38‹D :]S/"ddИ]A&e"bmiwbL93[GW[k9@I 6Zh@+; @ PTUHPVe'Vssrrtq5u i$PSWoPRv(.,+jp3pq rcf`ts19X29Ob  `d;](X\^K A!;W X7vL +/hN7A @w /̿9;ӷ53qps6ݶ+@7{{fMQTIVJRMQ@Rm\+s-?E?b4Iё݌qaވS_/6dLS4v$ͽڲ>^k72 ; Kiu>Gf"~*tdbk[`8una3/A%~|h/^[5+̲SSك?NUp(3pvܰk3p׼dBu5R|,$ɗR:-%!4" o.yթ^'X\\:.k;T^}_ipmg\W!AWO&68y?Ҍ|@$uHّFB~n&$64 y9@%l%XU0G(k?BQaP4cXU̓3$!`, A+ TetMw erBG%eԽ1M=cP  /ݫ9#\{t.b:f"o_,qkE \$=s!4LR%CEKF~DĄT );} D}|Uӌ/ \+zH5ͪL37r>Ac8TsBӲX<8:L6{͖UR @Shx?}`g{rd7)jYme"=[xDoPvyS O=I=XXqӁ峅Bڧxz0rVff/V6(y5e JzU ,^Laae}dZ& ly>f)XB9YSuN%hd|97ks"Nu 6tHESzk]b"EsH4'QȻuY!K'y< r.,bf[r&ZvA^ BR)fc&q _*U|_ycTn!;Ҿ(z)u G8ƩیD;e;A|`=<֖ˋ㸕K WFlg=;?\|-2#t&mrRt2ͻI=ٹvl:m., Zj@\YZ'oJ5*Y|'bo$rm@l" =Mg*=]C5M/}G9p:AyG RQ1Qc@2Q;5B0[=Ճk6&6Gfaԍ#]%Q)nBЬ>E̛. $)-0%_0n{\6}G&x$S;{NAq5E23NJm"pytxCCޘbƝ8rΈأԌ\o' I= OCVTO^bW;<7SM-KSjJ&pWԫ@|"ɒaDh0VC*'pR4MG4Y)gH\a q<5?-4*qҚAdu:8JkX/u$Iof7}2l% >gasE=IQo\<i3I#>K }_,9:QSt~ސm(Ωu\Z0%-.6iw 3W Cg2/0ݛj~ `^_a&/"3QlRʼND IYë5\|Vh Tт2H8h>ې Y2aY(E W!L =s8lM-?-v G7@5ZXv\IÓ1(PS=NlC0w:fۯ\SטخGýþFTtEv{}r.q.ilkoyP|p.RHA܊תrU8a[b%-4[j !˩u/ۈ0Lݞ;&bīr([Yb{VBHO X/Nh;94!?6@t,(.B(rq~3BXFZ(BܪUd]f-;/;(Z!"P a_{f<ܠ} e)=Uj':wʳc{]C䈼K,󉛼pLU˶  -] }Lv`z033Dꕼu cp 䉬m~U1jB*WXhc«wpA_]q 39EiA[x>KsΆs]*nkWꭤUaєCf^-$v3ٻ%O83l-&L"(` ]M(6ALl*)j1PVc *JYgBLf{7:hP5Pju/ 7+8!s.~|ong* j D:~ uT?=40<$4A!%6 sQ}gJtjN/j]rezzE8Y RGLLn+ǴhCg(CԯiYIBA`<'ŝVcG?eSa"Z'j^ vT< YozccB"y“m˜ Wd!'FS_N%um̛=Y}r_^G1Nβ?Kx䔍?\at7 RȔi %Nl1TrQsm̮[s^v528'& h \h -M7YmᬣNs)PR/ljԻ]נ[B1 7I$]r}D!y񆯏n_ {2\{)K6-a?XG0.!oH;&u66~ws(͒|~u+=ōo!iv/=4Qx8SwW?AP,IT"'Lrj–:/*vD9iA@" cP‹@e|:dFL5$\"a*̓x~Y0v&vIֹ%]L6m1/5nޅoEprL̆)AamIwB~`Q n-?>ra!@{׎d1 |gGps/*\I+BGw>:ɜ2d xJ(&,[AXcJ4󕷝D_NxFF#!¨Ip)IGsRvcˀoH݅WW 'c0VQ9bw$>ęGh۞+hHsWꤼxoS-dJS4Cai3n{%t2_MtM׸Lm Ȓ5)1"8_lp΅t?Dbc檽y-ej"@Ոĝ FǦwWڢOuғ TvT5ʄ(>r {VTH$V*M?qK,uzQVKFl8aI$QYiIC#%-Sf ;EUW3a1i5eԟU.VBد*ĽMG}V!k)T#h\uƣY!y>M"݇lc\ mʰDh\x{Q2c|Vudvr$c`8q UiLWRwgDTlH嚉 I5RFpiQ&|:'YW?K>_o~5EiKx=!֛҂a0Ub!`tn2/=:\eQ!}'^bg]RMۓ\؃C"z.|=O-gEE3c #}lO\0䬊YѤ& xˆNrYE[^G>vC3t:h\0H˨tcZK[:sBxRmu%zQ:+mcˮ'ܛMZo ߿MVHPNKР"XE2H=l҄'M<-Z"0L(Q Sm}0 9x [7aظ@ HtCV-/e< Mc|Bnn1,&{W!瑸X-ԓAu'09"LڔNa9[fɔ?oOz?!qnd$;P=)ARtBaԠ<,?MciP-<B_uBMk/7H̓NeGOJH*f)(氥9YЏ}~u`ݷ҄HBGٸeimv]|-b0\w\ӓ_L{ԧh[Mu{X>cl R8Ht!dI*)Q.l6z/Ez*bD{SIɽa ZYukdދEte\Ѡv;Y*?pꑤw, `P/(:٫\߄R $V;a _O4T">>bUC֏ܸa`w7غܼ ?KI/.AeEC0s*S )-#YhQ^&d͜ |$JJaMe}@,XJ mhzjP/q]kB;I[u!>L@|gb&k5_!Vj:WNvf۰ڂc墨?ڄJjXC%F UVnļ•5=ɛ"iqS6$MQ~̵g:Oc5Y}(MϓDtg4?ƕ~յ>kg<ތґtF{? I\3E ||0~Ljm^t{):}M ;C_KZB?+khInl)VEMB_x7PH\`ynuy`+=8[ f9S!kg]|ߤcbů]HwȐ@m *dŌ|@!$/RrNu A"IeR-҉¸~vv1jS͌ Χo,'3n\%ӧlP'CV nx>7avӈ.P efqe/BI^ȞpPdULSA!yUKbS;-!^#:*7'oc`9M>og>l&=ּGiW!D0ah쳂 &UdtPZ~U&p}lZ"wSo g[㳟{N-;J /L E"_OyA9C 'ߛ}L0^kc/ʩd4 *b)rؾvcEH:GEtt$#6'3ZLs%i H&Ͷkv&0&_ Ѵym <0BaQVW53t'b}+ ⓣ:p3 (` 9_j? ۝I&6!6 qz&7WD^]Uz8ؓ;shcrK]eY)cL4~S]Á[sg8,҆x+~Єdy~d6Z܅nȧQUr`m,DD&c)7TKYkb(./i9ꣵs&ܭc. 6?5vY1;:9o .Q[k1A_yH[a\` (?#H88EVA$l3 Vee|cpkR]?/vq4WkA.O60겉 4lSϪ 4k%[vu|Ud78w' ON,ʅl^V/^':kh^GwRY(2%d.L!DB}s9SA9;WǶ?!uV|f6d2\{T +@~m3 2xt5Q{!K:>f1L<>Fހ`0d=gZ(9VY:=褮~U7:B o[7 ulGäEm ̰o?|鐹FTuH̓1 Sg]* k=}猛,>TFl)"݉)ç8kͲaZ+jI nA~ttُ&ݺqÂ2{읅LhKj^~Z)+ޝyO:llitCQ0hgŞ(pR/rЗ _Cw0:蚾[r7|*o9O0,0y43ƀ lFn D:`Ow<.S8xc0ðK'Gx:.'5xfv`$`vZɡo$$* Vt% Co݀J u?ՓKrk_Xnq%"rkzA¾cSu*_ ݝcXB{meE/$g3Ç^u|i=J%WT6Ļ-ː+m!KW~Zu' ݆fV@AsoUy-E~ |i2flW]M'YWy; OhCFեB[7WҞ.8DzHʪψok}$ylfUW <]-]SOH0E^p)p/^N˲c̗4 S`O suhVDDɧQ;] jc)ɬ~)Kա"1 ZoDSoԳ-ŋ=žeLQTuͷMnehR?/84Tv^ü3AۭtRPNCH6f"7j: p&j&jXoIS^:޸4Մ27_묏rz`_m̵08 @pi8Q:/ՍuwEmZtRs"!myq$9~ p^j~&lco$Ns!gg;el&I .I.R~1Ӳ.jOqQ%d8=O;yvUnW诚z.Pkv&L:O\~UZQ>])Ug\\~d*?SPU }_*}Z}c2a+"K7?2wSKD wr!W{Q%Ip*ƋĕH[HuIAXrXJ>}cD&3Ɯ|M1kaӴ:t< _quX?;uhg0_ce:'1fYt(5U[l]1[]=O;ai޶%+wuwP 0é.[/?rGRp J("x[5N՗|1fI90 *Q _!UdSy tynVǍTm|k 'OќT:?\hYuCjSqԒT9#N Ϙ ˺^<^:!狼>d)V|\}F%sGJԊHqgNǘ%SdyT>ҙ{N3cW4H>84?FH§z[Nx7F۟ $JծֹA<$06jX HB#nQr +fC)*o>}DLS86<,!wN-iS@e'C,5- mw&>^[+%M?`$f*}@Aa+M;HKZOӞoguAr%G_af޳uU.Gؔώgnh' sp*je &5f5P @(M@w2)hOa6-$OUpWm/?mz9Sn#>>ANMW'ռ%gYIT [A\GI:B45 R M| -@EȔU[f_D*a's 䵾$"͑v%Ңng,_C%wbTЏcKZ߈VT#k)ߦ\Xwuۧ^,@1FB^=/ Q; JhY[CUķ"ӛBM.ܺ~`hy<#3sw}*1&cz&n)mP+ J(mJgVOg C>MFh!RRm#P{+ܤ!:^MUb|<rOjZˇ ;z2i ! = S{jU}E[h t>zv^-{ Qf50td(wKlq(ueDn38Kc5 pQDA'")pdbs"na\x^/;-d&VxSa,f±*>wI[/UUARDֳf.w.DB?~6uSLL(Cٷ³PxD?F֬0kk܋>ɬagaѝa*r)B &TBcL%AV|GJ%0舤kf%8c-_@\ŷr0٘۷|teRut|?TH6^o)|F@KR.Dh1Ae+ߝj,.IkmG]u`b]֏j؅8tN;wR3;켨40#9~m-QuWfQ=)~yV/HTLsɣ2rndOѷ6mxAa %Aioζl_x&B/9o3;P.dV9^s6h/#Pˎ(Pti] ɣ832r,J(ױl p^!'0P@e-#6#tYO]= ý/# =H|fв5?Dkӵ׳]56VAe{ hD[y~EC%S۵ z篽"bPI(~g]q@  Te2 D/ Z],7ёt۪)r ~1+?ްb`g~& c؝<ߙ {sɚNm.&B- CǔGvX#Eu)c[';^Ʌ J pz(\s!TiNҵl\NiS-5Ĝ=6f+d՛}zu07OY떾@:ZU5 }.8[l?4t}}6t9j"`vMVsa o-|$JSǑ>V: עw ZZx(8V6+_AeMo1d؇͊qy8֚C0PleT ;2v6n&)QK#{Eˣ?j~iӶ=6iE,8[G4N4-Z~42#_{}(u!v;,/I tH(Xofۤgĺ󅽷g.L&%Ww]59FO%hy >5E NK.z)+|)L=4Q@J5W ;9/K8!^PpoJ"GyfF|Nz ѧv*?,t60jZQ82& !Ͼ= [>i~સD%:jW2 ɱxbKZgS_ѡ?ZCUm2Gx*VKH0EӲ0A Te&p2W#)mW*\&oА^~R2j3ĕ 7n|%٠'[)՜9ʰ&ׅnMeimm562 =,p Y\s\8@NjvW6mh/f3X(y059[s ?8G}VUoאu|`\6z<}2V;H*[jnzkOZbM/cBγ0ɺ1UO0'(Xdv9& "vl66z4(DڣX[To hVM=S@Y!v#:oX B{VHIbu&vl#C+<:=JoQ,l?}\9xC[#)H}hާF&sYO~1Qp4ZIAjL&IH5|C% Ú3>[R Zf,Mq<̕g9K_]qbHVP֌QELsD(  F3cŞس[ 7Uݶ% _ˬ!PsW#)XqycSsڗ'R{j#lg _%w̑E6&/E)UO)&Ӧs[ "ضTU^bhb7'@+\ƍ]>|A:C͍/d7Lxg牼cUh&WҎh`ӊ1 )!lB$S`>'ħwÖ8[*ױ-3j2W@mJ0ߜ-Rͱ&fr g zN=w^$,c57+¸h6! '-.`p8]՛[E;73[ɀ92*Y?:L3׌D8$XYF3J[8_|Ww >AA M?KS =-n"4 4v[n[ ݊{F}-wp|@X^w#TI+RmYޢ;Lx0QiB =d!eoFg(UDԄ|{.Yu]p@m<,R$7˛&v#OS&\{Aq-,iLIHV ;FZb̓EX7"48; h x6R`\GpRxxťD2rNVDB-Pj_n]H4ӘZڀ6US{]AF4Յb5sD@!;Ɖ[¿(64ewxD|״/-,CxjfpWX:\-NUEe~<}V,s."}2P7i)4ؘ"[%#Ʊ9F_Rlbr';DYG)7^0Yq[OeUU?- fpbxHV_9s9s-3dpy@0oLw0m/jji+f)J80L= (7:D[{u+G4:AI/v2ΆO\?Ld҅vϯz>M_9 /p/z`Z$8.' b7ꄈgX& Z0DS k7GZb mxk>'S#<暽jFU$]%Td7AKZ; 3̱8Fi %,d2)kTOzV`$QIX~4BH\䗾_x72s 6ᏃMhA4Bz/E? :$=%)73t(  9ɜL8FG\u @WQ9^ΊC-^XǐXJ`j~ȱߪױs%PlvU&0 *}bB;!CLZO܈e WgD9Jvy @Rÿ8%yj?Y7grY6 &ѱ?ؖ`V D_Al[ "B/ yPzj'YEdm*C9H}}9^ w}Wx#k; ~(^QhaHt~0AiXz𰝉"^rSk]IvQWQ8gl̯~hy$z=d ̗`(JI^jiVG~}(TT|jwVy%wdhà yH{x` ^U6erc[maJC2}dt-etV*⩸nA2䊀݂> 7 _HrTe ᐓy(#7G`#C[)ں֋JAa 2Oߗl!)%l5Gό1(aq7ݘjs ɲ Ƥ͙%? .Ci0{~ӱtm@e){9Tcc $" |hJQvB'~N"8CڜЕBiC\r5v@){yMTj)9Z[a Ij[(Q+8u'R)-r4;!%(NRJms h)P[p G|Qv^F,A)*ޟ8(-4^QVWWnF#'X4C)?w;fZZe1eWcU_YL56}1j''d/Il st7m+8D~2t hdŀm? iBVk&LAuwE뷝`xWQ>%bj\ []ٕO%Kwn DL;:l ۔c|59Xԥq;e2NM!6kq"͑+'.v~PBAO&s}Z(&w}ڃ? |J3TQ-EGF,eronp;KY[v w8`r3&`wMlwa6oBN ^T޿<)AK)?m4`{`  V&-CR 4Y쬋3p"Љ]#cc7YDwYs[$T1ŴyFeng&@x,(`?F^6qǶU2mB0HJַ֯X4f5X'?x{5YY_9U!ax7E~kx}UT0%%J֐P@ioMU7i;Ꮅ)?zaHw (ۋ.z/x"/mqnLJ "1'=C]^>I8N T5gM2w*a)FGa>e@Kd}_o[rz=I$Ơ+=iϦMgl+(hO40z11EoݩJl,VRx2Y\1]w#8SLlTRJ9[wϲ2/^]ЉWy4Yzkj}`ʨ؅8Hzǫ?6L}-SZ%CrV5 %)R%Q<_Uh+ֈjFFS ]PX\t0l5OSxnLV'@b.J;nDGd6렻8@qI3L4mhr9ЄύyT/N2kEit_|X3ntNt0e2#D> 3Kv=!S #",BXl=Q;Q\9QB,4cS 7(h= z67b-NNm_ŌwOڷ*(Ɋ|Yq.>*-~E 0/zƐ6V6MawT|O'efR7C'PwXn9XmSM4@kzmQ w.&+D@UTL t+s<_|"|_Jx1C&N _|dZ\3XbPwHDr ^F8?5>@ u}ENuNJR @B4UOPdr8(`d◵,>[#F+b)-dJE![.d ;xQl` jQ_}O}Ss“dn W *Fyk1>;raҺp#(6mxqA 33af:擱fETN~b6Eɔ<N8f<<{nb2J^nAck"X#nw%Ʀw}]@qh'P< "~]tb05+Fm {O,@9kٗX66|Y=#Qڻܜk٩,Ou;~r]TA LL_ Εf\˒$R,m+jylrLql+&<<U jۨ?LfEv/ R3EJ1;;=Q]<^]i4{˩*d-CHOCcBȽ9L-ΨSާ/;";.B hiV 0㊮FHk' '͔uP a#badhH2rWپCbewyu淦 1P  Ktѽݘ ]`"nJZC Q dPn,Cs$طHMQ+W*\qpé1ig&k5ê=VrX$u㘜;jkHǽf#7dв<ȷb{;#s !1\1n/KbQ`FaCw@ 51 Wpo s1DRTi^9"K6GM¶X#ꔗBYA:hg<-%`S>>%[^s _ȘRy8t[9)mH]'(bM0{DSϺ(_R7wZz5Yܥ/^a >ubxe ,Vj5d$S20j & SC>cFON)Sx4ެG~i_T͝/G^-eߵ i@8mh쓬^¿4O]gm31p:C#1@آ4-B%Ci%2l$H@[)A&Vx'rܣقOCXmKM 8^qz|7*Uղ^At5$ռնZ (|N̨5P8*b'JvƕF Q¥|) @O3gI|b4zg9LYDs% o]6 R,FKXS {NH2'dH2jG M鍤5I~1ɖ#; Pb,uO*גs] 0Vs(~$҉}55_ޝFbg,ڻVẓNx({r\)H`-[JKXy!;'G{<KKj`@.*Q*%g>+`E` n׾)E6|^r h;k&7-İx/Qj`rC~=%G26 P!8kca>8K0 B I*]Tu[gb(#C2`gor/J/sE榆Ti_XMil07-gb *ʼP֜% 0M\ps(*YO%'"=\ 8vHG>[ xJ7-Zx'5SVv6gIRΓ[͘jtXZۖ#5TJhd)0V_]XLB4:#ZNy7e5팄jQ]JKu@88BeX'Tym1Áy=UtZOȘԨY=+5ձ^ _ibBU(;.-Z] dk{^C^|\:OGj lI&ъj_i"fS/̍RWfm82XqXؓG&}$#BiLcqE@H|XWi^2;QB-?%+$71qZv8 b'à-Ul~ǸƼ٤8?19 +sA)WG=i!Ҥ ZYO$5{5Tu { yw?M/Xǩ`h9 2/IJ?յBOR5L.[u~i1>6 CJ&y|;f(z&vo]:P:Mp-(*Cbx A*`m3sHUR%I=_e{|FJ|վI4!k%@ A1;}tL]-M&IAFw*%3 ,Ríd)ېlC9'(/U)ƏWv0cԡe^eף!eW-ɦ\]@HE?j G^$Eg&Q^E%i#pP6TmIBel~Yw4fAKV_a9}83RۃD;n (ʟ糩ቐirQLf4,{?=۽Ω/N#"_ ů _b&]KgO#lNJa##/)X 彔< t}>zneisBG^nޜm _b AK8COR=@F?} ő421esX #91iQ|S]iJG+R_J_WD/eȊj>X6&Tw)T_O?-gY"nK|}\8@⸻ 2MJb5,<O? 㺓h앖G-9L3GӜ}r584O |h؛6RL/V $p#"̱U r9A-JebOO\V!/q [zvF!ˮǰl&7~d 2C`BpHp5j՚Z;6ixͯ-3ڼF|cBz/d.d[s8W&۽ ,i+# Z^lK7$:f {⡨%-JCZcxW6c}=.ej+ӗ4VlLCh<{a_N?yO!ת$rW~)fI;fXξXW·ri9U>~ wӨj^?],jY];lfb;g96N T UqY}3hUa AՏ̲8χy^D-bd[3xi!Ueofgx f>'O+ u=z(nXmڛ5t9T>J"NR fpGkE }] xpڷxe1oLGFǓ _JT !Dתy{|{np&wK0LJnt ބNrqBGi;[^4òqEPTc[k6ʊGE*Ȇ -4vx籏>!jFHٚ/E*R=0OhLr9(SܩHlC2@K[eAwJ}3 ;pZ0GGA fi pU I&+3/ sr6,hVJ#PӔo'[>C8tdme|7 g&/kc<68kw+㬕众V95Zn&5ґ߿;[ciA'sY)21|iEw0 ,k%X]/ s53|㮥Itc4ULwt"٪m#`Y b2 s.#K)y8$<uf[6g6fV\P|M;?jtu%zƶ(g. znF@p~?̽Y ;׌):@\ $^wY7zyHI#Nţx0R֨.d(EIGצF6.CA7l<+iHN c$*FcefX(1OPR۽IoF9J0qXoPk㤴3`*Ҿ8 ľQFYJ}.$(}H8Lei#V܍i0CDn<i]VjOAlbSKhOex[t+۹h-,OMg]I#]"Vպ @n:v:9ES(wưVP|=^81+M 2OMZ\I/<NeS5D3ܡy^oݻ%'o򞛰\2gF8Q[AD)$@锁94%_ix0{a 98DG4VW\dMYKnSfVIBT OX,yJ]&ti;)'Hm'3lq%o%+D.ȟƠ:ZLJw6uJ&G۴[@ü;<Yi6W -h]LQQ~Hm@?jTDW1ArUG/=]uݦ$y^\(x*s Jn+3kSɃ c9[–X'* .Edf0n_pDXhS7 +'sD ab>,z L zB+4ɶ,hr#*"yaLtRtz21SZ{od${)3T4eE[= o7<2WW6┌GW(}UkYj\q8=d5M!Oj4lZz)_\L*ZY.(x#[0fuOD1M(uqؗ$<4l,ݴG&\AP{I=ԏ0f1b1$+E)-2!TCƴ`XeNbvq#7[ps 3#Ls>!Ҡ1r_8 0abȀ.ŭAo˛I]З x%d7v{^ KPatٞWެ*Fxba\Qs\;ykhd ^y+4/}z6V-Q[+JAI:aqF%Tlg79ý|?pgە@:TЮgIo &U_]zq>9;PUlP< )QVJ~:nsgA%!+mɤDJ%_¢l]e5UR7GsG+<9;,P"vJ㦜fDcl%\ཨm5܋6XF9$gఆ2U m\並4RݧZK4s8-r\*K 8CUf^s׉Mq:0UsKلO ivހ-vt?Č0QUk|sӠˆM &(@(|ԷbĪ kKZH¨IUqMD&&D^_rpPP:y,a\݀̃JPzlrBQ, p0iDZ[1'FSUNNT˒gK)dlHPg{L#IzMLUf6v?0!:h|~np~J 5e-`V"(nMq?M+4MgVbLA[7eqC u Q6ԒF P5t@<]_Nd)PRa/z{DG@f=uݤ. BnZ@ w~!' endstream endobj 70 0 obj << /Length1 1878 /Length2 20267 /Length3 0 /Length 21394 /Filter /FlateDecode >> stream xڴyctd[v̎m۶Tlcwlt̎m8>WU9WXI^(`JWq3gfWZ;XEƮVbƮ@%@#à :&^y#@eJoba[X?BD,,]`O@ `loag(8x|(T9$UՕT>9::8QU5uI:8ATWUoAPP'\^\MXM[I=@g?e2?};;U@e`lh>5K+ EY)'HoAz ONۿ.@࿕4v+VNII`gle 77pt5vusx(9;!&_"wgg+fl/m:ػX0Agͬ +HK}4=; y',&b0s>TL Ĭ>xrupbmca_ Vf7ssdTrrJ : + t=M-_?qtpۺ́8cw 9fV1.pe7wp@iFcNmf@s8F׏3iQKVH8OGc;+[wMT vƶar)YZMziW~,_*?#eѻ՟ @42? `RP֦m󗟸`l ,6z,F{׏ςre:8>-!H^@Y`_?*;3d`t:[9Cj GFW9>{;JF`Դ2ycWg+O])` K= cX?ffN5{Ok??­.9X5T@s3Wa h$@fta[(xSKC0l߶ڒ̔Ņ4ԃW*HOd K 㧢ݏ_XQnRI*7 =.0;ۢz vt? a@9vUX ډF*`Λ̾eWaYXlw$."Gŭ~ֱT᭭Lހgb_ tڍe*i @bD]8DLiO˚e4f wx(baٛgƢͺ:]"2Ĉqb3\ܲ͢;A z%2<$./vtuݔي!B -tiW"&0:mL3^)vrjF`f`ULnRRCY;\:"r#$a"W5:k,`/~r4oynfB~7HOke龯cB] _hl3P+6pkI N9ʃ6ჇB >Ǯx {"sKO:ȔD@~n}"l c2uxڣzlTX'VK":- pc:[g?$y^}MrP[mӄ5ga=6m׫A\EC7(q)93BVHTI `*ʑU*GRz cr|~:gԯ*]4}Ifֽ}o`r;0;t:r4V쐣Xm?!s6n bw%ehӰ߅o&IW}4Kj/Z48xSV'*F陷\l{뙘ZhxN!X =_|kWi{0sΛ%8!t)S|MvQR{җr^އ}2h Iypװ@ɉ`5m9uz#ż1;)p 6 LӚ`j "%n8bd]F|MîYFF3yf;ZcN4Ŝ]ͧ5Pڅ`o9yī!.&2*gdCO}vays56e6ʱefQ,`VcaqO\6!Ȭb$j85*k2xA<W)[vr ˭bX8˞ت%5WnS` "nEZ*u;"[8\s3XRcXZ-5Vwrvg 5rc&KBY8wNo+s /J~#~A%LwuVu@=OL@TVʂA>Wև's.LT2N/~$Ģ0)_& E1S_vZVr@*7&(?V N m_k)ޮ:&_+ BP͕o) b X&JS7Ϻ nNdBDLH>ICky[*&DoqΖP/rTSv,w`pJx7W .qUF7-tș@D(V+OE*Da3o*fo %?=ۯ#o^S꡶#_Ch6 |v~w#4W-4D~F5aOb=Uި ?~L6(SMG ʹ:KYmlʆiXTB >wZ ;+Bvp'Ӱg |= x姆(p,o>8-z;2Nxn(V`CË*T5tx99 o/)j{$~x\r4Bu1`rN[k/[a۱Ea(+2HR8xaGJL WACIGpU> GǸH~-r63gE{GV}^5OE#~1ؔ4  {X/P`vGeR^Y2 {5sSӕVrV[K^'2=_Yy8GB|EE[N[V"XP/ XVO70*?ϟ>dk#}1v4[)={hچӨgS*do &,cU~Wwj㥼~sK6QUKH8i-0|0 ;y**,j!\-᝱nPp{I2=K!tS)Ëոh"Į򩰎!ikNçkD%N7^D{VKE6Sg'Ls,&^rd+-mo'Tv x$l J;\8>QI:&*A?٨?jޡ-i#b3U[vw7%Adl~* c4IQf(Kovst')FꧺPK\|_=1\*@3%0wǩm<&6ԉugn]).2W˯i݄+ʞ2hH{&u&Ӭr]bضYzU9_:V[wv՚]|nS+]8^gصJΉ5&q-0i3Q~R<c)Wu T8oޯ7ϑBj77R?a,C }v8L# kOc,&w:,#䦴*fVͭ9 vg*|BsLX*QɦgFz[-)ڣse-(o_wMMݪYIIsV/<xłMx%EB5>R1'PV(LD~o3MPz?=, $]6 N^ipxmd抉ƒ#)Z[:\A itzI}/xVC!<^PJb6#O/ܰ&-:61pG;ίE}0p.VQFUKk_@f8UG GQpcT>,ۻmT/AJg)*}da8E㄂Qfgg[ _?T=սȔI͆hDiNFvEXdrZDAĹׇ.c?L#BJP?>`"a_4v7K#>:b_QXKal^F.U/.03t07RqUFS _T:;#<|b2ToR- o)%4jj)&yd&tq};Hڞj?ٰT|d0^;ʁ/,:~Q-_?^]S*;)n#d(QrEgpar%Ղ|E" `pU΃]ybu͉VG󣺱FpOF˧?ӆ\]zxx`&#]JoИn|^-TZ *a@P OZt .y .+!DtBEr߰ۉ2޲C&hڑpw K2;mw0f&IAR-7q6q>R]|8@atmWܝG1]#:S6 p[j2DJ{eYѲe8R8Ӯd9e^JкiAH!Z} ;BK!^Q5u ^RB/?v:aq񭾀S+DFM0L[L9͛w7Zd,L_7=ԟ)~8`tevGn@ak ݤ`w@+-/4 (NG]:K)OC%G]Bn*6~ ]L^@#"ngg] p狅 >ZNSN)oG"9Z W]U,㣎tKƐY`KV7 ʒw| 98 nv9%B_]da%$f`@@|C*[?m%tՏz?A;? 'm̡{>xgtJM8N>JRM+go<]} Nݺ D1AK2o}}Ҳ3)R.){iC=vLZZ(Cfyz쁚NZ?訇帥}  sP9$ClY$YJ :7*! $>{F}Y{ӑ`)j}LfK iO`m`wq`2oeLR28pA\-4mB\`M=ѫ6[G㡓rU54sˀ jp.ÆߔqeQgFz~iriI"NHmR< jĎ<.c;_POBl J.ә:+mkȭ4o"ʶp{JO+>,J}PÆ/ˡؿUhsH#Sމ0w:uJU3c?[+|'Vq7ecH柫!d%Vd49&3LkVEQUa5_}ࡻp{Zn67.l&l$ &!EU@V<72 "Wja 5#8txR|U+:S}=v YȃoG .,&^T((xV'ԃ^6Z˱a4&`}jt*ɫ>B^$Iei'm$`BUtwP* Q'`q38zcYW,T̓\f/9[L>Z]ܘh)s{XE!ݼ53X|۟T(ǑOn8uy ,'9mbˍ3爨M5pU\ ~RIL6c3BMtZlSF+7'H%b .. b趏W!C*q޵68eŨm˒fm0n-ʍy~NR$A_ݢnX4Iӣo~VRφWqJEl\߹3p$"aroli ziNfa㜩^S 7lo c-[9ٴAR 277FItID{oϾ0/9Nr f۵7:[߃]eU**l5~TKOpxaƍ Yv24LztL䚹,D󂆁ڕ&@|w><#" kncЭ2*)& &X)/ yckq@}I$઩۞8/%=+eZu35.$kJԒ< ,u͖DNbSߏ*Ԏ\?$CkEUٗ$YZӌ:t{_4-rf/\v26GӦM VG>X 0g ŗ~ճ"N4Iԧqe..q~a:8ղ%-zToż+hmwƓַ*Vm݋yj5I70%W| MlAsbMz2^qC= MP- l#f5b%IW |W>tpvAjױ{<:Ϡޞy*z)` yvSM8IOVIx ,93xnwLFEltB_F›@;eVEh=a\B5,,i=( ALRm Ð E: ۩/nc-2Rw | ov67O 38N@RE;]g?@t^n3|IDƆbk X8ȣZ(c{YG)J<4_%(ިjãiEߎ|8OM-kr@v5?71Lo}C$YƽwOO7f~,QME~kbF9O'2#-iTPn<{6=lOe2Nӳy͂6GQ!,hiNsL>c -^FDsmJKAZ/W ׃Py4{V6c?Ico* !J 7uBt~2䈼ML oѡeo[[ l [mB%nR;Ejo3#?χ@=~FM/}wtQP*Z—Z2Gcs WphC8W)$qm_!<r}EJI7x1 `rq:ty/i@s[XNTzFT􍓓G0O6Z,?fLk ;}Y9.^M5nT[B/:X $]\9BeI:C'MAI{ X %wTV׉㟴a݇Bh͍\*"?jzflõ<@ɦ&{ YkogX 3U=ϽoW;? C^yd m?#Yc͎SіWgx”烓X ƻB-: ?xEnZ2ik H&EVd3q}#$iM QXdvr1s KG9WqG= Ho|;LiB}f HusϤ8ҝH#&^X'j/0ݔ\Z~ P^QvuNd.f<$;@-@O$i1:wzgغo;7Xw4)d4P"xV+c/:!Gj%e# #ـ,֔0g0I}2^rV9hWf YxLS=O^7gv_/G w2LEƯj]So*pÈdqC :7["٘jwq`9o<@&IFۜxLI9s5[V>Y{GKʃ zFlhKu<ⷉn 6:&-qA?ّYĤG[}toWoauQ888K _a8ӮAym 3[ؽǵink-xDFT8qDE RG}6ЭNDD W/uCjJ\qP ^zd\I7L1R!Qn!# 變eàX7:`&Sj{:x*Cj1L/UZr19Q R@[#5ܧL|_r|bVȆrmCpCe.Qs$Y-w@)Ph1&<)[:d1ɻP~uOzC8s/j UEpxMU68 qÃ$lDxb QG%VP3yܦzߟ uK p&|.?qk ԶNvG/6Gvys=ȣ6 +~KB"asv~뻇ItH$XJdS_ w @0U>P3^䖭K"mY,+/7&r,ɉ A'&״k#uǡؙ>qOz_;u}U* q+0i0hѤ̚唅G XL =y{NU/Kgtq>i }ahQ9r])P!L7|MBz/ipLX\gD7O(d".Z v ^NIYGtFĝ[#BٷA#ZJ,|ZvL{lmmm~C˪_βՙ*)zS/Q|@:#qj0I]2>|xϋC:$9^K3,l%y mp?  d\ˤs9`ote98E[#z!:MkhUsy/^Ӵ L:i" g%NOiPdѝƒۗYjo=p_Z84` RI`,Lšml P3,pjnƄ S6h LXVs{vuEz$lM@g`9&Vmb +#)cfA}uM/ #WW񢓹WsR땗%{$k ZB.$[[׹Eg1ƿxV1Tv{{"Y?V$7s %vq/k,sHi]T8.@u//_MtB,_SS?åo4o|!jelg≱ ";fE4U} jh͹gu]$ BoHL{% qu˄BPiBdzjc.a/NgQnv+_A`rG z!@(&$$',1oZl 4TH|m0cZ(l'Yyru7>vxW-d!W%tr%Qek zx?V$CW)$7VDbݨ2P2ǥXO*3/XWd'jp#~u.<Ǚc}[X~Co5ȽԢHڎ%h H.inY۳EzkbYʼ(պ1s:.ŅEi_g(wF7(e EQ^jIgS_a[OU)(ވ Qm8UEƃ>YYx)R^@w CbsxRES[ Zf31R,,PR`,DDzΜ"ѷ>KH&2OfvFeRܡǫzL7c鑐mB(Жǃ3F7ђ}8} ?Pf a;GFraz{qHAdP ^ "YGĄsDIAypPػ ug4lB'iTuu_U:8'lag9>ہ:BdX@E}wːe.9o ڢI7u]Ǎ*B:fxT2z i{mCa^QtˤA" O_NRμF Í^u)lQn绤hZৃxG_d~})-Q |e>m3]mEwQW4 oa!dMP}$&Zyw-U44 ݨ4XL/6o8xւg-n1Ԁ@ I5P%ybwtX66ͯFc%l}H91K&3#\ߏ;K.ț',&ތU@zD83(n*ZfTtR?hHf疹wr:h|@JIwhvX%wC?j?_q׫+eMdy1Fej1}54V1xNlo߆?i"cT^+IJɨkEn ·hh۱XYM~ |&e^C?ffqdCЮH~(BPE/n&{p קГLKǃ.A"F>\j7퐷~tK[/[Տ`ػK|t򛨮Mpb'ǂQV[PݣoRpwSB%gBWTAߑ}OU UA^CPq}(_ .|!5Pcѫ[N (1jv7"| QiVD_rsÅgN }G8PF^L=] ӑdBQ OlwF@ge%jsó)J-E9[[IQ2C83TkEڴKW(ˇIlD1D \[~ڧQLfG$X#P[m/!%BDY0j,p["fT#۴ ;ʒ[`?z~]{6),wB#ms֪h]e_~CxAStW[*V|P覩ic"Kl;Uߡv'niVvhv_OYhʝä)8bTYzƨD3Ymu(42x>lk_Y\ҭ*1(B>I(M/D7EBB/! ijEP{d(Rč"l,2C"#(lGDZ^B]_79,k<8.dnHOzS4i+wH ,[-DVqv. 8j9K"Ph~H''ky:j퀢4YwAd:F>Zij=QB|-VjȆ#ƍ|/ _ "'#kk"<6F~jSac"^ڕ U!RCXw6z j ɛ5Ŝw$kxY8e7ޚ]bo{0t}g*R6$WXAI5#lǁ@%dBۈx#$h ]"ƹʱ;8A^,%:(n+%+$+{@IK|-G!yR+;+9{q\m 14kcj#'$ Y^$AHWS}BP֬Mx;i*P3#91х#I6YۀapI3ɀ& d0Yh=!";1wsԉ&lޣ6Yr?tN!V/g%em-UܸA4a>CZX=Ƣ{2Wg99YG*rZY_Sgҥۗ9D& 6MMMG '"jt7崮BV=7vbbҴrLS* MQ$?KO,ź߮xOFx` ^%u: HOʼU֬֝F{j- xF=ٿš9/j4/´vcU*A[1iDk@5Q^SqD?n~5X$nM@[n0DߦvM!{WD?K"Fށ@e׈z ] 4% z8=/2 xE$n) >)oZ]`'g(jHA|[lhyEZ6n02w%}fdUrxYvN}iNlhyN}CrrmpyF/0M#P )ǫٷ]9$cَ=IP-vD^:hkqq]! M&kf;&$9Gu>;7=^(]:uPZ'~ (bmݫ/2T ߲s7(e'ɖUۯՕ& ?HrYFY7Jw߲^B9ٰC"݇"WDiѶ)? eMry ]7V^^f7,خ{{ 5,`KjoGW?-m[T4݁VݹڈňwJ1XiNj⦬7 l*!K~l~)PwS MCAt]= Z(3goPx,'( E-$Kw2j e Z=E[j{9H,;H"F'v"1޶]#TIg[m|Lƒ)K.Y_"QJ'r󢆑1ګ3G<)ĖSx4Z縤FwHƇHesnbʫt0kmOO+z:xX |lEPr~dw;a]|x()}[!*1w szz3J:8ᬿ )!&?,Ģ$2['5Z&])3eO w]BMS-v)!lB@-;ŽLfSPĆPG.@)HFAӧuiMOΌHѶk,e.h@?+..DoxG) aw[`W1P~d[:CPgzUS7Ղm*TdpC44fߘy*8в%a)4{S*DLeUf]2L*C\|i6VҊVƏwVƌg6Ua-5q'By\sv?4b%=`BEVd9'Xm.RO J'*rF?gK&&aߗMw+^ɒcI1IVQֆW SABWSPr$ͺ5S[tKDL#ۣgzQaC|*??#MDMMMF +{z>JR+=M"wpyxU~eІy[U׉ze\q W]$,q?=]BUULpc0eM"/R#170׹6Wɲ݄FxQ MLvUioE@(=fefϤBr3~/1Rx`BQ"T?ėOWT4RGj3Pl_=M@ S.C8ӿ|Ъ;%e_%Ȟe|+ĭ*Rc>r` >N ЉșF% Pי*1y[Gxܽ72Sjwǁ*aP%_!&y endstream endobj 72 0 obj << /Length1 1882 /Length2 22538 /Length3 0 /Length 23754 /Filter /FlateDecode >> stream xڴeT[5wB[Npwwsn> FQ5g60JtFVtLiEkK}+&:E\`fm%p8 >r?"`b@+݇` :+yk{:}7 H"lmjgfb ݟJf}+#$ =@hM-e:@EITQ ("DEQX+)DeE@UZ@&Y>eD5E< 7Q|0G_ 6 &v&6S658[ۙ>he1NS @heo(?>KcjZ_K}3+G=@/ ho@ݟ2roQx2m wO}>1}+G{?@{?gffMFPV⫨2d?cEWz"NF6CVF֖aOcNv WVV90X:%D'o . ?f?AxX-쁞f7Xw{}' O"X&Ç?VLPT{jdme 02Z;|HM^_-,d-goifÖRR|f_\FfoL,IJY|h1s}٘!KCs+=4 0Hɨ~R+Nзwe3CF@`vH8:x`(' A/`70/b@/ #Q``0(gߐFL?~Td08~Jv@53Dw3sb׿>G/?]X?T`ba`ߋmR\J h`m=1K4o_]2b9m[${%߷'"ZZ[+תP<m%bHA`WK QTp4K^/]fɧX2+Wu&52z"1y&Du-ҹ` e oi=:BGpzV/7sTMOdU 99=q)ڮ/cw:v2rӕ:y$ן(dkd YLl0c nNKX2N&,IXSTkѷMtK :*dZ^I&L~jg 5r˃鉛j-gEhy#: DEh>M8@\H`Y-ZQE[#W 3^xFxØi{eR3n6HX=7SV^F'x#IpJ ~pr`Kgg'1ߦ%u`˜9ST b\@33/5ssF:!w`H2M: m@7/k3?zj.yOlx.IbEjt?_SҍFʜsDЊ-q?d>=4uuĕZC:}_cxAmG 8HVHӨ!֬*`Ш^(SCsxqF<+$Fp@0z ]$~etL!vayE9ixDsDk ) %X۪S+?~Y п"3cTQS' ޒuUN9"8"o#X UNKR.rɝZ܍j@-9C%=l+v&p ^U #U&lyV"ZE3<5͠zFRw!@[;c4W=8ISy}gYߢ|҇qd .p;ͦ %^Ln]`đzӴwJlZ̧ yނ FT-"-^Ey2[Iv*J/p~GA{[ЍkT[[[]'|s\Bh6If ;נ%a`2ZO NLBmgUq/3'n۩.5 h"/ 6G<: ;Ud45uJ ,'gq]EHyI;89lV5:1;Zfb1 ܧ_ÿJWR\&zWlFu۲d VxKzgz3ũVkbTQzo X! o9;Zt`U#vç; s-K=~QgT~,D0w-J}"48 X0_1=z%)֬lzI' %U%i%$O|?>5?#}tb(ڒKSSVaҸmy4ZzD 2xmB~0}5jxQnmu؟2S-t]PLWnc4`}2YؼJ'ohiк KU|#xxJ1CHs,ٞzi[UܬcƳYч׭6omz҉lPM../ rvAT:vV_'Nc`a98֬Tc&Ԇfqi'@',1~=R')6Q,g5_ c\GXX1~DR~oֲD|c3"Fw4[p?f; 8S{k[#SQUTΕq.RJd6Qƻ`C ۺuEhn:q[eGr;SNj3/X`$<lYdFu<ה˚q6]n}4s}gee(yټ}LCzkwG ܇1'wsjFNKe1gc ~2ke^qCzwxE%q> UjCMl~潍EGoe@Y3mSU{Mٮ2M#Yf?#ejL1=pD!oAoTD!΁Z)aj_Lyv4Sb 7*f`!(OU;Hh-Ob=$wjl\o`, Q#ΦU.FJU$ĸ+>ݟj]ACXx'3"T\Դ;*6U06"<&zj8)[>5x$`Wrk7J< n#̯_۠jUF8W654MeFͅ PK#w!J,5Ĭj2˞fB8RmDFH;[HJ}!L_tB:Nx_Rc4˖dkMYYHktSyc=~rJ8DX\0D4ŌY*F\Xc5c4ezo'#K~||:l%S 5 ;hBP.\u,(2'?U* ^o't l2eh^Z#ƣRfXė1=>ذ;x yGȗ }Xi%GFIc4Sn@ I4"Lӗ}U" sgf8spNiuPVWa6xe@^xp#jZgWYB z$} tc ,&`)Sv?oPsɖ&ͪZ])w w oCqdbgDNB,G1̄⣨8xZ <憐-rC0hgrDnOeߺWQDPNZ@wٴpsV{ZeC~rD?* nuB T|g`a&Uh9v/c?IrP[UY_POwW?%],3Z}c9r+tl'Q$YskeS iz!.3^  .k_=K<Vf*gX#9rFLsi5XG?l΋8lM.dG99k|3Uۆ=7Ww%YpO][̺{-JfW#y%:Id{74 u/pȃ@tL֒81er''e~^nWQQS{*IRBi׺"@e}[9&!Y zyNd(Iy pkU.3"ɷjYTK_/"=9?EE1$WRU4IdP u3cTzT:Jϸ ri?thoz\Dr+6)-ZTC6] &'Ɍkݝm_6P+Ϡ($*wǛ]=j猎Q䣀F<-q-J(:OSX43-׵pi>iɏDF('p8jZ.ͺ.Xf֣Aޙ(nlmWTz/V/Rآ)饑>otI ydXޫķ4 ǶqXЃ j't,e'oGc&h 9)or6J胫} F/y]fX:eu&J~29]wشJ|l~kCUŭ@[W=X>:wؚbvY^[e@͆&Bvfg W)N+/E&&bs-9Y3wBƍ'kqSi4Q"R8δ^mcCJΒbL{ϣ!L| BMӒ6Ϯ1Nߩ_i[/[H;~CqS ¨. o\ m>)ybO)-`7˛+!C!m'Y\Z t(6kVYf콃 5_|\y-+D* CZu9*m!K5O.ۛgn5<^>WKC˄X(Vg3KR8Q?mɟ +Ғ Li}(”: WDT&)ra_P|H3K0%{뼸7TZկ1rhM͖ –Rs^`/)I H!{`5W2U-73m^ojC'P ɩ#sI/9 &í&6]En 1GVU9.$̎Fxz])85Y<)$B3nt/qxe2%,\GhŞmj7qbAHJ$-.Wg!ZO]ҹ< hYD?ƇK&rݘ^dYm)N9T]9U|oE~XBOp/YJi\.G!P Th|J[R M<5uR37,\GJT8},le$pB^|<H/J:DK$;}|~\pZ,[5.}(EKThUjt@h= >dv1PF:ffW ';:k\=~B7m6]Izљ6J286Uy0)NR^C7 !s0N{tAx"fp΃OgwcRsK=R}aTQcgҤSSgbVgooV,PH՗/H7@(rf`ꩉxLگ[ _bSz?Pes Ju>e$x~~AIܭ6֫׫TV3,ctK9:wz9X\U!mLҏ&8/_y} lg42@;X}|IbMP۲w8àUPs/įǕ5@WO5mЙKMQ^QT&U+vH!3ٷW~$lA ;6SH(Hb%UУ}\eņ/{{s< I$dqƒ!VJ\7Nͩ:"C -OCgB)9w#B0'0Y=Z0}( Xbj-mP*Ut`C"r*S22~7vĄO$ A!飆(6۪o1;My)F""tekRpؓJ8,l=pUFSdZsžV!@ R+O8ݕ(]cHS4!/*Aoh@ p>kuu6s|.GGge;+Щ^=@4,1o\u&^hWN_#q}a4(+wX=Em>8.z+؄N[Wɠk/zJWzQѯBZ~ 5`9 мU"sfyik#O4*#(2F1u'>7jT"䜐 nt-(TSOgFJecn L:3.$ ƥHr;St}'U6d$|N2'Qn5s UyQR (l"w,!oc#VeeYoCFӍ}ۀĸu?\8[ S$vu "N%XBnqVнH4ASRdGx-pT_%Dۗދ߫F!%:Oc((I-.Jڟ51nJBBrwz Wa/+g{FEdXu(Q,YmhυʗHT0/ )R0M9*AŒɸ7vb0EKCv;$d @Nl nZSE=6i@$}v[Hġ2Z>3yLeqV8 ,&٦#oMcY\Fp+5XKH ore }z[Am#WDVAq!_sQTE }(:I n d@ ějq TNxf1i*:̇UB{*#(ss?g'gˤ9 < =V4g)Rtw1<:`cUfΡȏڹoh_D+t}D:S΋j>X9GOH2KK<4'}IMtEgY }1FVbE;ұw+.AK&W-Za$S#;OԉЫc#E" hn3]: (ڤ4QfpKz܃4U2a`l"V>eV1Y]#^ڇsQsf6wKB$];֒u5cA Zfiu MP9b8S)5Q -GxSCE#sQ+qj#w12S=\C0 L92/&h#2dAU6GnQ2+*SӦgtmYBCA!Se >ߵNP擟t6<~?K+ͽ:xD⟍ZU_^\a&=c,m/v4JܙEJЮ=ATi !Jv*yƇFԢ[:?[PE;_[ګrXbj=1 Uu>Δ?Q <`JiHJs-=`ⷭS lD rF\`C+mrb}:am+}V%9Ҵ6chہ}%gikp=(q=)(Q8$_*r}>R'^5}=>/Р׳]FΝ;SFG4r[N.xҰmj@,ƺ0ox/BMy68FX,&DM2EV笲zN|E꩔GRcbE+wbk%ɬ}iAyiwiS6*8=Ll4BDg^D F3FrZz` yLba# u 3.iC{ vy& \"i:HdNTTAG 抁cթٷS;zdudmҫ(8_bAhnJ5C[wVr\4tPF939sj = _E?bɩ^co7L:qINB iq!7Q6ePѰ,r(NK Тr2+C;*!M₋Vg׌P(Q&4w$zvX2kx4΅-Y_aT6wO4l(8)a/ m؋2y yӨw%%#8{CKBLyuںmk([A Uu r(caG[=ũ}6͗Q%Y>Fy5ٯ`g0F;ak,jP6iEp,Qk[_H\l 0#!Nfw$հm=w2}$U|y* }ڝ+ |D,a\'^3f7AMyѪ/@gMSj$Ma`QKHa7_&Sƒd@ל=Y꭛ ݑEr$ng2Fqy7ኑҟla<;+hR1JӍMҜt0@x3P dԹUz Z]t-Tug0bg41vƿ7%qGjxgS NM]l6Z]07$,TƄiqxbƟ8,beΐxGJTlsH7[^C Fĉ.Ɓ1)= wRDc_ $= HălaƜK}{+2ubq-I>Â;* d 䩘|WgD$q7촁}v>3e,N =LfGZ63݁r.Y:^bBDs=ֶp~a=C}X 6SMgӼBpWYD1 RvٚYd*+tٯ#h ?M`mWe <#sսaH)ˈz;Ѷ<,|Dc xUwuzp݌i"ɺjگ6Tz!8)}(iX~,0lpSݛyKd+Hun}a.AFz/<Q%z%M 2d~̊okX K2M3 qnR_WHB̠͢Y,KugK!~TfIFM_룐=o;sw?fLabo8LZLu2 qD+9j/iʗ]hNp欖z&ޫqؙY{!3)ʜ¤t^~âa݆Q74g7큋ܷN-߂99"b uY-Sp.>+5ռ$K4A sd,@T;MX2gߧOS#E2BXm?V8+JN#37&Щ,A}3K12Dǀrp@]&NN=+ж.5= A^xEd=_pQׯtYQlo3 )[5cs pKlIۨki@eRe(hR1k奌v>< l]nTm4Cǥ j1\M W6,8TH?%Pxwt#|7̸a7:#vW!e6!ρlU.{V#@JAruh#,!> Wݗ gFo$'ה(%Wu^<E!'-P[-ѷif[2tOh1@ݞ~*[v3\`ՃnL9Q_U^.?hWy= e|NT~NQj4 hJbxT 1sh?xVq2f#W֢sL*T|ftk.*/eP#ßWE6(q*A/:+528]zI"gF\ ^ȁ5R\c-L߇\d9 SC?_Vxշ]#!>kɬ5,VbF_)Iq%NPZŨa圕ĩ lG7%]W(K+0dz-ćP>8(@t*zH/2 h%L-M7\5>j*'0qD&5'0'*/#\&,G`)Їȏ?tw|I"ٷQ$۵"?VDj삛 ti E= Vcڽ:{~\;%ٴm}sWNϊڝ"#T7b`?p!9nW!ߐߏU r'A=}Ru]NW?`:wZ$Byʫw{p9wڵF$i6lɣFTSA:0&m:e5y$:]i~bՋXZz=x<^C䵶l˪; jhZ#b&O>4 :@7wԹ\?cF̔eO Efж:򾌍~R:+yq U~L+K2 f*Ju~r.?|/'vMM֢[kP4 Dž/S $X$PbmskǛ|Ǹwof2{A%q|+T~M^I]U"DL:Piߍn9 )[ov6A']0X9 l-[Shj &oښlg -AW0*{>ԥU3= YխުY|"7#n>%O\7mxY]U".Ƀ>|h3]3uXHqz'ȜmHW7(BZTT(ti87A<}sT.$(7uh4sbS:y5#TqMCbhȢZJB0=XpAS\6oV'BndU#"g!rNfEFaw6k(R5 B@M&|mWY*"eu_ԹQC{Лn##חZX!vkH~+ar;\[oW{xV[oeяwo1(x2t9`oq"6Yj-WQŘRsLҽǕ# :ܕjFs_+xՏ)ja{~PABA;Z7{ 9a+(bG~:3TK-nь RWrXO?:GAHnt_tb@fD^_XYa(F~OaIeYnU򌠦%I(SW62H>*GF4Aྒྷ]#K`%75y 4ZGLRapeR7hkw~+zG˸inGDA>N+4y@nA% L8YSꂬjAh@?㖠(ׄ1y9-,'g?U}.V7B5^E VyǘފyHw<m zUΜ*EHnR#c}]g ÈWڐ6*'ݲ*K4 ;sIq ھI RTo;vg;eXܔ6J8|M9wixb;ju$;4z~X2۵xf9zM)Lѻ5eHo`1 Rf"kIOGDqe}=9~e\]Xzc ܹSl!ŪӹN]\+Yn!5G*P4aAH% )/L@Uzo:Gqe`y#]PArmϒµDtGCx??YWoOD,eޫ&0ʧiˁ!^/-?WBÜ>ư@x糗Kbusk3n/jr$6ţw?FGr`fs690mV'$Wr!8qaaSjD(,섍um[Vt{(TȾڹ1+ u[ncizȑȲag@KvsΎ:]Y5g3\vζ`JB9 aJ?]ޛVfIܗ2Pѽxb9p^{$Bb 9^sj$!yuϏR!!9P[ 專L7 \GH1+_fL' [ g6R+ȶ+ u!{`W2H>%5v$4}J"WbnBW:-6Џepv'?GzTNSpxmLfcOVwoR_z4 p݄ d'ThȋG^@r! $n7[*Kv:lԦ+ fwC7`yqkpL?Rjx6tQP6.q p^! xF!%/Qxԗ|o{L_cÅN.ȿ9Lmj{_ߺ H =gs^Ǝ5 C L Gް &wwB= yruu0zRV0cdlou]p^+޲̿c˜`*Z(xR؍RU ;R]qDJy]Lu ].TҔv!n|nsR@Pldo&z* {IePgg'ޭ,ǖ}J{W\HLqqOe/ͼAw&Mmq"߭ : cӐg@fۭ<kـڲ۸C7$btS߉Pq-@4eGX:.%7IJ`ZrNuL2ҽ>' C'M Drt. ЛC x"u;|Xb>stBp {G/(bm8^g$ 8P^ӿlHxs_d?"~'8V:Q2ezvjk|8Q)q6 /f/uLG7q^^Lup(moS"om.n|nsPzB;@k'AݡR'XPM ѪÛIBW\NZHn}G#PLtmkL (H'2B>"Eϒ֍ghЌB|Worr6hon_7~l4mXID*GjQg@Y{ GHIQp.eO{;OM(SM /80$V<gs]fh\Ɍ~V"e }u`3)=ِiWMTʹn$k t;LPoOJZ7e*c'uj[z% ɋ-9Ck qQb^c)n2^,1{/5qV; 6N)aQamt_EG09@uBoz2PfHb`7a7,.^8--Rx-ʧөX BQ GHBCFQYhvbN^*rRKĔ 0Yk Nkt v~jڇ 8j ¶XXwc5+,IYxr+GGhBH,. b6u`dPlnܓgF^D w/I`D3M"+#bFVDZzVM&Ij] z+Aڊjmg!=HuD!DKU.tAU,0^IuSSQjh$X Ȍ^îy .jŤzLae3y<:eTx oAktݍ 獘թ)$(6綒onHcǧ\)CxM[|8c~-Mɴ D捩J4%b_rMIדNP[shda-јHBR8Ic9N L]%gĊAc fWP&haF=@fAѫ/x]Bg. {tZ%]dX'~W鑛]wSlOuOYqٗ,e\VX+glv誗&.5`r'C+llq&i\$N!n?B>+}]FѤ? @݄SʵCKqJ:$I൚a?C<15 i,>wnK} soyi)mTMOIxg~o`0]0R " X?SS-)#?l{|1{mJb-4,K"d1Vble'~*.i{E'ډ/,pcC2pTj[x˴l.H]|$(r. fXfT8"U4)0QJb+0:IK2]<~u +&@'}?N7;0Vӌh66_,wWY$JfF1  MY|X,R3`]F:'OQ%CR0f{ NiRC3604 i $'Z73'jqA7RxSq5ۺ@z@\x5k&9}Q+ȍ+Rx'nu0L3k'{t*1W듢8u?fp["f8-u:pڢ]rF#\0cWN`O&pAe XeWXi|5ۖEazNF(Qj{n(xy-duǔ0hAEgPL1v\|wd1r8qͯzvym!r8Ee?“7 ӎi1 ?I*8*87aIC`~i9L1J!^fUbW,"h7o?W^#h-` m\ATc[ }6rjt'_Twjmg2|EfGD R=;?k!yϼp32=-B-fɈ\\x=ɛǣ.3-v4@<0`>_<ӧf2I}◬1 Wۄ<_ͪ@ɥb;|zViʶVR"I00ezaA/r:[ #X}[m}J7NW*B Χ$Ȼe <22{h*K+sER, @+r>.[ 3n7MM1^ܓA/=Ubɕ%(P+QZL !(1UcYr苵 Eqmzn uz66ˆcB'Nd.B"c׳S (.BC*G?1 !.Jt9TY}JC~^3w*kӫO(䙵_PkK6MNuPD*QRQ( . WL .7z(P (:oo8l1SU>9.j hV㑣2.W#< T'd 51v`jfvOOl-ŹB9*d O6UmȡGNF9%`N[0Y YN)f`>PHbI9Bx1N@aTz[PБZt0Sf,~˻cʊxfU/a_دt|QFoDE[kYy^yH;͚匹cri CX%>k$̔~70&Pl!3_U<pہ3xrp*ǛfHl@aY!35t`i.\Α4 \|pK68bA|HX!Q1 4Q3x˼0P>a ߰VFW*w6 0¬[Q{O4.Ѐz1[֔ gҎU,/̂zT[Btcrw]JW=2A]{ٖ"9ɠ/9IGlώHȇGN+a3G ?+,sGI ] 借\9 S^23YOLm)EI-{Vzokד٩<]kB3Җg m1'61:Sn}e[ }돘ڨ_=^~?/C`Z8ws6˛&v#O>b^a;j/dΡ DLBjؾP;ٓꏪrwoeM , R t)`ɿ@ RiAZV8"Z+_=Dfqk-mr"x<=-&/d~sr^>;c$&Ku`󰚸0nozljI X!H>tT 󽪎ۯ,L,eSτڀR&.GT h5XcFUSvȢ8yTUr>E2Bt+= kiGv 1vG%?DCFߠeU>i?Vf-YtHhxNКį)_hK}Y&kbEd$"Vˡ^ZzL#zJ i:40 {>g巖ifNsInwSKVh6saZк@r텱'r穭Zc0Ei/V;H*7Bjv9N|KRoc#:+RbpP?R4jT7{ o>%xT3i׈йC-D2!(aN ^5-쯜5,GE$L6ʉMkEs'@aD"twX$u)Vq˖X 3d0:Nklύ["c>ak -ħcL%oqΫ"ܫiPQ۠Hhg'y +hMAƙIŮ. 6I.qxkQsUF"Wل-u ~o>ydum,~gOx#݊ hw^Qn<z Pl_4H{ endstream endobj 74 0 obj << /Length1 2837 /Length2 29952 /Length3 0 /Length 31528 /Filter /FlateDecode >> stream xڴeT\]5,H\ wwwp(]BNpwwV~ (ҹ^{+  ['zf&<D 4s6t001!: ,@bN@9ࣱ8č@N;@dndPANF`7H Nٹ;X;JOl`hkag(\F `476L@M @R 5IĄT@u:@[03:*8w835.@Gm5p*s'';FFWWW3gG'?T- +h Gg[N)9 c#w_N$?B8ip#_m ɕSTZ:m mNNΎlo {6.Lw/m{ [G G'UL-;3 l *rձeprs'w=a19(rrXXXL!5؀Y;"Ok+[k7513PZ&?63 ݌gZ~2x{ځ֎@o S prpz{o 00v: S]߮*5l&@SFx 9gKZHoEO7W*,%,܀&N/!xmͬ=Ǥ4Y|XO- V(*E?a  [3 ; <,OfH#- sv~o&;Q_(qE . ` Qb0JAlF?Es\ 0 .pw?OST p??AwyAlO3 15u$3ltxn'L!wL`&`M@ֿO'?W?࿲ED@nl`Yq2>?jw t#,̓y,BJ|JaȹN04e⡗RŲIM~i 9)O>A_֛Oޚ( ! f3/vPdjMķFE:Y&^Q薶27b8X--NC8>bF/`ʼDѦMUJJ4z;f6pR"m) f-djb#C(M$t8źY]¯eb,[W1rYļk>^w}ZBq^F idߌJFPaxɽrDъ?zjXF2=r%jb.S?ĶDZ_U\h)B Q7Z篎)}gh"*]+aESmZXB@ց!oApٺ3`zM]ARGr1ljs%.;TlѾY[Nʂ*≙WsuCh>`S0)aЍ 6rUO.vS[UJXN ںD-ykHm7#ڀ|ryN%g^) rZMa]{{/AUQk9zÉ)g5kU"KIrcY ߬ F"xV=r))I58QQG*{ZQ  G+}c**mEQ>D߮+IMGAf@*׍~`/ " 2YqTҾ8 (FlV4t&p;۰F{oۜOj^`x_9g9+$IZuy5D{ﲫ/>`('ܧa5x楆^u1'wVS {k*0]5MvBZťگÖVލ>n9Ɂd.;?p>\8%,Y$g{{WnuE {p)"_q(ɤS ~ `xhޚUhrDHOFo}A.";ׂ苌uJnhbX&pm肫"p_:2>t*cP}O:9sAMŚpRBb}qࢯvs|mVۘjMv~ =7 *ucӪ D.'Ř[A)+"n 4v[I控%.}wc~n(n7l1;-00=.C֎"!U` JICPgRk2ϬXFGju'Lib&4%z^l1RO:JH6Yemkϔ@@ׯ0<) QS!q Ōa/6@g-|qŖ;$9BW0ŵw jM8'/Ł qDŧH(u\D lN!rBMٴ`K @>mMV\vkpZ{zI;b7bf8q(ޥBy?Za8gDV _)d:*.\oG~q[Xj;g eO V…Fh]-m*@:xltip9NqAb[2r7`2+I^JL:ik|p[.rwıTJ5)8/d~6^r!uwsm\&92ѳ-Rƻ5g̈́L|<9x%T`2_E TX("nL5#t/J @:%6[K5@쐆y3%LY|AnMT@N$/{0雛o`th Ulw$!x+%x'$;k^Bga2{j&wg,'jm)@=A΂A>w@eKY9i;uEk|\fZ!TJu hA6nKw2:}%߽4E~0[fW]UhxE%>l཭-> 7{{l*/[Q#x;oMK-BY? ~xʯ}g {̢W|FtN}#;%ߗu+ AvmlCҔ>jK0^Kn54cCbudoޘMAI?o ~zHK,ݽ[schLc:p(z`t'e55[l,NѤz$d\݄U|rBQw]G6F~rѳe[9!F:}JU'RgӚG|aiV:W:xՀ>=D$SdиSlL:Ͱ0f,b#qj$r/0ö;Å$Ӛ^s!A6K䝐"nI#wqr5)!k˹*]9% "1ڈ֚V&~Ԣ*8SDQI0_d&c9߄ 6Ђ+i%kC t OsҗP)\Z:(}^{4ZKWmRV1wli7u\G?ك>F >g;I Ҽw6m>$Pe;]v5Y9P *T2"]aݧ_?Pw銔rFǽe*j@'|ѸGP䨯c%2^EfEJ),ȓ5*#vH.E|ƏmF% =iF>;Fm9mK4P6 fn!-09n46s٣YøwV]@nU$vEx9S`-+D7:B Ns[1"* ZdN-9>WDl:<՞2J.yͶ}j/T n.9;V O Z dPG{^Nx5]<Ǫ8>9!Pfdi(GǷ8;:.\/nBL^Fo&Bj;'kSl)@'ruDC)v>Ғ#YB=CN9IG{2d<&n>fsAVNb;B44*AיFCLa=FEY|s |>uN.\U3NU ߂c{gl<.F7I10k\a;,=gNbNX٨o)Z\pwٜ*7.ݑM(~& SKN Ca*=G-X`W+d=C52g].fΝ*NPIz@.DjuH]X|IF( ke 7a W"YF#Hhķ`:tG[TJ=@F#[N 4By@dyIND">]ePGF-7O`8S)&}=Ӱ5x#0HR'w5j6\?ق\tǶ6KbefRV_?/'QԱfr6 i<gd,85A"d'{`rI^NhCkʿ#BC ^ߐ_?'?U! ЗRsQ"o%x;7egR鳩@\˸coW|WL38-DJ%(ϕ_Tpþ"OA4q͠s2ba?Q~olMïprYN0͗XsqElUHPږ`zIލ!]y6az\$[' \1 XFb3"&9O R-y ^2S_ڼyc2{?5=ݳ_/ SkE`< ~+16q%o^ģ ĥ]}twoG>TUG97gpl9k@|6hsmeݎ`%2]?Mi}Ͽ{=Nۍ֖})3_4!y*\73($?oHIώm SQo4>^#Hu)D2Br^nmL$׼e|oOW>n+ Fʃd(r aQ:?.;^ 78V&jZ٣4M]ۡ Uu.i9z+Oh#N+e+ɍ֚MJ7_qMޗYXݛb|z][ciCLKL7%֜cj*rwl/ ULЯH21 =b #W3ܚD݇B"CA#3tkt\!FY}"d3yC\6{1`D ݾXEDb/;tx7nf lc5'!7BEPw(9{jX{U^ì BhĘ*HvD4AQ(5,c]Ǻ^\kLAy̡N ʓZ:J?9_x2բU!־*x!W)OQ9% ""EL Bg!arQ=|[foH)唠Z$b1N'tgBK@_[kCg8 G~Ϣƫ5h~P700`nFοvuYt;ق|!W޳9Y0JJLܫdBp5탺dvҭPvT. >\_r4횏V!3c3R\>J_=/g8:w@*dەW㎉ژ(Y`X޷} ,uVqKbYq#oRnRCB$ w0ڽc&hc^Wgq'wHUfan$DYl.axŪOLdN$]zA$_ɶ2W*|ErNJI? CiK |-?DWҫ/F`8K٨T#ڋKD Y[JG^YhY-~ AIN  ڢʯ|;{F:JaDMEZbN~"5/alz߭a6Xv8BAd[cY=8* #)nUdB{-gNTIQcãon&@B+K?4tXTGt s/,PЕإc,;<~{UTڤ 4p`!:j $bƔaZMAŒϞb.X3Q7B@B}ڬ ML(P`a,[\PdV??x1%<;X+Wz]+1H)6\fᏹ?e2 R/fmioOJ'Z@0Zw=gYd^Aj>伥[ aR\c.UtzWRЩ93B20`a9 SlWˌ+y*> 0uUMOc\N˸l۵[2$%pxva ͅ#T0VspIϳ ';)kʮG}'~wiu@R\?,7GVyJs {;@qd9Q $Ak+U*heV]2]K̴ K%&r9'qXn 8B}2~x_% ̽2XK/O$r.hL_~CNBY;ċam'aH,j/,%p+\n敭G33*Lſ/k #OU|Õ1t~Pf[+Zػ^cu8!t^P[/݊ҦQ?%M,q@rtDmn8.DM_& ~;oЋ~l:A7qAH?^QKH;5jU888ޞɥΫ{E\)6+-U^"^Mx^Y I.ZE6'a;*ph~{´;majrύ E⌌WTN! ӛS?ʊE.Abmy ] ;<5Kz|tK\1?Eшۮ]^cm1> jBvJK3޵e/iE1s iR´q'R\/(Z3~dD(o\y|V- ˩U"4-(5Ak4WOՇrRyOJ\dMwЪn6߫G#,;i4]+uW@P݌f\Vt sY}X{c[>=8N1%"n|GnҾf71jATэGđL>{j[T[yH7mrĐ]g*'"u-" i k+jT/[Z>ÿv@AΪ?3OQJJхc8=tj`PI䃟1r&B#[LfZ@}ώy h6ÄT_[~51zQèn!D]~5X(|0ɮda,nf#SpX32O zx13mEw\/a_=کRQev@ڶ`M]73jc$<yHtǡjٴ͞}xwr̀0y*asJ9*g($] JSuPL d'ƍS"J8)!uf1F( SS>GnRçϪOuUO\Es2=xE\6TJVYw D+3i&D%ʯg4<[1^ED_> %tl,q~)=(1S~f7s֐wjnϫ|ϡ ] O8xp糔9SR/֑E +M^Wem0jd\c݉nK922Gsv(f-/ EJW:~50\~jUwKcI&;Qw ۔ƖO3mQW<_o\z,q팫޿쉤}SΫAOތ&spQJ"nRGc%8~#@wVXQC7@k:Ra ?ꝯFQj%--C.(y’R]Edp"s#]d9~tCM&E@hZ/j)UPFop&WñN _)ZYScث.[_YNjDDB=I h"bP?:$3 E}j~?-ؔϵx/Ù'{S`u`| hI!c=gil 2ϢWMo3T;jvHׂ*RMl3iEeR[@H)|j1zX[>9Er  $_aa͖3Ơƴݹ軺dfy G EP ^; nD@g[/VP\kr,F|^euu埬?"r{T?ھ6WK9tYc4SXiq, ֞NC_My)CN_MfZ.qWζMbIF|O5Fτ f #Oqn18)*Sm)oH|NJ^l2/P92i)[۞P${j~ofBm~Dq@W0'# )OaMƦ{Gne / 1iLbtՆwՂFT4x$TU(T*ZB4QQ&utǛ(}8 U os;e5lكƏP,a;"φ,2Chm0nŞؼzȊy>j3 Ywh<ܗaο2^^p;&Ȋ9}; 6L{z>"6`&r' C8]";1d4ㄕⰒ%ū 8&J٥*2νq|=W,UOBVG@=HwW{Bț8 e|=Vj>ơ#ׇE֝mmnϓ0Op*n4LQNјx!)PJU;Bܱ?\^msHGKA,x'3e<7;ELmkfRvNj: 3s6,:wYŜ0͟Aب)NHo=5E''fL6*Xje͡ǚEQ'yL{Ň>7W~JBR ?1`^Ux#0dJ# *^2Z >{pHړu}`Llye^yy'Kf9lN$VSD3S;@{Z͜ўACKdyB џv o(gXC)rzXjEvPvI?# ~^= mʰWGk_+Z2"!ů{]({8Zs%K"kb3{H? EB= cz.hЉ~Wg%!7V]}rӤl26E^ -dv=Ch\R[A9Cše@( nQKxDX/ߗ-ˡٞ˂â /"UR_oMDٰpM t'yQsY>%ug&:lO7`DXjS\a[s'iY3~m-r|_{Nk*Dse/ʞwi$8m_h=(NyK Mdx TΌ:=Y j8.I5T`^xRyK0"ŊA&%]< >2r\.ppbCO7 NIij> c%m9ɶnTM0' *D ZQ7x:_Q3hh n}?O=#H) @͌Ⱦ!`U=A j˱]l?x.cÿSsv9'QgΝ熧Rf !VT=B2ˌE ؉ AԾE7H>\ִiҞV¸b‰ ŐOIuoَY1$@|hX9 (Ajm-!Z `a(͐=3joS éW_<\^/K<ȎK!(K4 Z-N5썎N튰m(79KͿLK,!{:]K kGڛqOlt8-j?jdr}s權rudFmH ]2-`5O"7IFerf['Wx~5|AUfB,lO3ZiV ueŕ5N?=B~e/_hl"/BIJ#yB75roBoŞuݗde=?z[3`*sk~.?x{s#4aqF}kxQ6fPZ<BU#=$Q"[ঔO y. k1鐷5hCGMgӕ4/X7#jH:{H9~bv!BaE8I}H3K̯0I-&L]~oÑhp#^w-#%Vj',;np|=dE3/<O"qϦ CY LyABz/(9e}:abI:'mNDn{ZPUβ#U-%N1n4h| J`Ɩ҆#.&ܥG ѴmEj] 炥rAh 1t _'BI}-sb;?\E7Y DnPydeS W#Iؖrgf`F &N2I}NH@ْA8b '^bӶRfD/KcؾvoqMfN0 /H(㰉 }g׺{kN "WP2WHKZnyEXvqHuzոŸDPC Ϛrb~BǪ@7@M#٢XKQmPrTBPv'Og^5HR~Ơ1_5^ו~Ncp2k$hH\y6^':Xέl/ZX1{>Zg!c?cLy cAö@zbBy A(Q(;՗1[pg+ڝ% ̑KjVjwΩE'2*R;+¦~!CԷZ 8Obo/QsarOډj(wE%i"9?a =fj-Qq`7e?k:@)iQXB_Dۘv'lqLܘ5Dmْwo݇{d'5m6!Fi۶m۶m۶m۶m۶m=s|v:Tj:T:,"xaiMu2t 2WEiZ3DtcB6VkN+rhR+/+R@^T→]y^##ا9=?ӥ>ő@,̃"8L:2mgw2Ĕj pOYhIʼn g7:|u|ca=׈6a iK nQ VDqj'`c9 =^\h<]nj$`9;k$!|\GйHT^K%bʶ ,c6qcYFøM2@egM]Ͼ-"BT7{ԥuCd~W­ i/PB~-vvqyr/7kʥAik bXTM [v6Ka0^S@Bbf7WD0vriL9SWC7VqJr4 Y[n [X~EVVcU^X yϘNG&Hje|}GIeSTuul{D>]~r{]R4[#YDگ,}^qZ˫k_^},˵=*Ԫraf6*K(a2cQo\3׋g_B(h@ G_:T3cPZ_0Rsb9HώψaV8i$Ĥg٦ x! f@ &V}&hB_-nfx'ڤAzRۖܕ\eqN {?#a}= d.SpE6 37[9S >sy~TG^_ p1۳గTb2H$rynp!IS[~ZLܨʧ NK2w%`K ç/)`VO^SwG!+2)Yf7^ LYvơssu]t Q0ueE>gɐ'Rhhjmh\ ^q@]DѫA 7pQsyD/^&z a zM7F C$K3cIv3<쒜D]O!Y#eo9OK ˃N,+S3DkęZ9[gOmkFd [P @d3-?5b:P;OQ%jJ~Un2@ 7adtPtRڼxKs[h34pz46L~joK8^cd c& eA+BMga x7?{ҵ޺[]VڳDmW,z(7('<3٪Ca#QϗCGHFdψ 4:!;S3 R2I#  [j|w m{-=K%寢BHdzœ ;{+t׌$is#`?ɝ$7oP)A\ζXv,&`(Ɂ s&j~H1&׬?fhl2=+-WXS4TKN. #zE5Y}A`1n2WFJ-RhđlBGMQ^<!skc`/ 3>SI>=i%Cj? VϼHIP+eԣE7 r>hX'P6 &T˷!l-AY?9{v}ZY5U^mAh2,N d5DU]*4bnFAy7 l(u/iSwmNMHNjADyU&'\>L(u}g8SYՀ!c>Ca!'[QȨb]7Lwrۺӗ288IZa>hŪ?.!$y7rU)Urk&r~ŧ Q BHaQ1y-u)UO|徙fU!c E/2)YZd3VV&$J7ܾPaM{vy!s>8y2} ]IS!&RJY/oߟ1]DkOeCm<'b.u`<|cm? _oM16V*u< Bd PkyWr\:tW=y-z X~T%8qwB#2.X $MyJ}8i&@@R뒎Z}1 e}4X҇R_ yJciTm+SK{ /$25K";DB܍kΙ6m_lruÌ6i`{/(2߷.}d9jV1N˄ YlPi$'w5eL+=MٺWr :B[jEchr ٍ}J})[*F&L >j8law),Х-! $~6:x=RLOgx{ g$ٹ;ӎvfvđ٬]%")+ y)1[,3h%&7ꋧ& vw:L:@+usYo?D1ߏu(ҫl?|2P or iatS!3>׷=2,)Wxsx.˦0O=yrLq+(}Qhbïmf װXCX|O>k d} My&MT <`# `g/"{G5j/-@2߭\/ >nZ^:`Cdg=oT(hv@g!G*%b y%EÈ6}E(ǖ}u.'.NiB&*8aXCEg(>AF*~ =i>%eK8\!4KhH^rE!6aź7;k5+l :[yf!T6Gg)Iћk% >Ggbq~*A(j-qŕ{KG±4Cy@%'[M SJuIe #+)VYe dmzYKlcrgTYy1@~dm@%XT׸I9ӕ:;2c>7 ZF9=DLVIt ywPjo>/]^k>UK}OWC2p!c݅^Y3Zz'$ZQp#3߳0?&AéH|MY`Ax,w{JLhN̹h57`KcT:/oeZf ? 3L@LGo0;3d)V GHA(#[CmɌ4Mf_a8jD9 p?pY|!I\v߬ "18 ui e(i}_x/1hDF(e^G=8 `iyO58Y:™|An)A+*.䭰&)_k6q^;bvF }b;2Zj0i3eU~ЄQп{Ui>Vy.~ Y/S2҅1O3Lz}8u2ڡyWKȘd S}A-ax~0 qJ?ӷ֍eW3Sqe,Ou8.& 5ѣ5r%!˨ٛ b *UbǨ*/W>`ϥU` @b ϊ= 쳨MJvt7SURC!k0a XY\DW|1}x͌u'ߴ@h-ʑL(UՒ}alaEO~B~(6h/* [# 12rf8 ?`mw*7U +0 $~OzzѪnόYx> }^faE^.]572|"ֹؑtC>Iv:r!'z"W^g=Iy~n";\#U\XF/6mkK" wQ mt?=(!XHu۶! 3 0ùJ5(AH)m< $\ףX= tAk<~vB7ɝfg)u't&[[oJh0-V R#-xt&Mp @[,C{3c0,p"k{S5W!e{D1%#ۻ?3>Z #t)!6_g+y3͟0xyG#殤qy=QCx4إisfWvT^!TQڄ Xv/t:J2|Zkvv$-)6 PJ0]p]B9>9o8I?e4EA01s‡ڲzhC ͜MV>3 zHREڇn 48&p"֖߯z3 /4| W< 0~2H#WOz7yX z^7NřԼ&K#jqŸEyXTrxv^nLgKZbT UMR1+A.jmdgq}?Ps<܉ݚR{j_e}N0Jm֮RK@Z{k]?<|DO#Kj2z^q(V{m0T{ M bUD:4k=zA$ZZ66ӐF>,#.|fz0JhwX 'QnDZ qr5q `i`Dk@<ChG N\I*Z3%Ch %_, Ȥ"&Vv| 6rPdܝr@чpkpp?E^{O"Ayt4 uCcxMDC[[׃Eϡd{mVoqMFw_ ˝{ ,S[nkOs'.>۔r&wWx *Lp-=֞\fNec4Ӆ\~vX<ިZqƳ\":GoաpaC$j(@]PZeuQ] _~(,-G_-PCIlϟmO?&mJ!@^ah¡?ogY܌0gulI7^*ġ * K 0a/XSPݮԼJVY"u2:M`{$wpURJaĪ!?G՟.JSE–_?7Ba t|]Ku`] T +<\Sޖ@7BVc3;D⪞U;겙=);C,QM`t~]*@glւ~}YGJ :n%w^+_^Rժ0(E!5/~2 wy45+z+OaHRFj[- V7ͻN5#0I:-1 ZfŞ 1=`@TÛcuIF\,g_AjU,S`_[RG 8 ӿ\ifwh#tƳw)jhH< r.;h'Ӭ R3]*3E-/OA<]9(贇mFEћ]l ̎2"sЁ;Lw/JoSP=&Q?67'4 UM]FC}@?>=Ig#vgpxuYP`W.c!)3^+݂sy4!;&o BtAWfBX'q iNwyKz"VĜh]ZXu}bv&@$U E9?-pK#ơ]Ȗ7w b>qrQ1>L؜~bܫ4Gg˕'Y WU:und!1a2,C/B, %PrZ>\3jvLYeIލ& N;vM?/}擥>x1x76dn Ϳ'|iy=.޿~UoȜԹR5_'&|i3a8IfSrmk .Jg`GNY;.SF =@ e80DMAe2d[ 0` yLMd/wAZya2obVw.|0:_`^~\I~eb @t@EB =[9œfs̪uuD֢aɵ]{I(]n-\`k!1#Y~J .0d"`K|$q*[В'رr_qTG  pp 1fK`,z=6,%CON^y͡>!|u[U5e$bHOsyGSSWHL$+G%6[dJA\ :Clk=5ͳ8jLjd8+ L)pLl~O_fe,]mC mˑAKr- CPPȏ)b0A,FV@O-]4{=Ay*!I0nkGaېp -2a.X:dFm3fz\ s9JeJSM`hQ:`a[倌TjWa2q45tMTsCJr3g&@S-$:JGAJUo*6F$Qu~0-<D _}v?]954Q#e<۳y ut>qRc*-~D%6j{,,?﵋CL>~Us*h v 6Rck#H-9#݀qFfQI* -1CL+aU_  ;Qmfw/->)\Up4k%킛 4p]GrZULs;):ZʱR^RoaΔl4 k=ҵ Fn% `üu *U|Ճ+=8\!ff<ԍ(2^mT0*&`(YȚi˰8nrJΝAC F9'!@+),iw ].$+72WJ'DpeLk~D9S$L?A!@Zr /m;쏹X&sŶ%{M g(~bC1_۟X޼.QߩI#2{<mke[' RoShpՊ?jcuSԨvhKB˖^|˚|y({L)Gܢ[I%bYy4 I`|uN@jauuޞwQ`tsP~I8@b3y䣜j>WYrJD8]R@/sK#yrQt ׭B;&E4J ; +%NhFd#]q@Jbij O#7v܃'t̲Q YN!4cȵ5> Ć_zJ*͌c 1b z)Ty|=sD0POT#Y˙'D_Xli^><Bw4oK<M֓.ZiDД1%W dq&9T,+B~9~ ~5,H4iAplYt ?$oYŀ+n*VSRQ*4U,f2 mc1q&H1q'ȤH'C D|Pm9J5z+Oɖ`Y|+BGd4z{r o-?╆C}ɢw)@ mIO"~K!(ˡ;~I{Jlsck*alVF6@2{"glѧ5B7%};9de=*t; 鉱\!(3d(&Tb:a тOv\Y;!@.W h] b=x4(^S.z~O!ɗ 1B(cB 2i&:=큵$$.URpbb1 +^vE)4D$E,UOR[M$EwGҀa~׉wm?P/5Q;]_WGRO dD=x-؛ǙbcoMM‡\UP` {Ht| ڟ<+C"4d%[t>|pt:?_@ 2&!՗gO մ;,sKlSlŷ߬X9XHXf, B&bE@1d[WĚ{0n8`/×ִx A =u!K޿sL E_& rjx9*EG\(b^Š*S)  d77 (}][7L,a曊LjKRX)ك?KfI'ׅʯ]40!S_]k[vAn#1Qp!3w~@@IJtD32:a"un\/ ;ίKxNQ{=%4{l L}sx*\f QAccьX8 }j@)}^0$k-xt?q `^0ڻܑNs~nXxKNjët!˃B5{z'Oߧ{f*hRD5Drk.dr+D,y906BeU#]6^8c =p*<>| +ռk 0 (WJPpq/Ŕ 4Of&K7".<>A,2gxgC/YUwޏ )|־9I]\Z{&+IAb$̾ rtThaOV+U|hN)X[8P f[Uu~]PQG"`* 'K I HD8mJNOS?9 f.~#A\q*^7G7I9+ˁ@qf*1^Z"M¾rqxuYd#<(cՀP,x6pVC徃M MͷggXM+f'C 6? W&u?Gt0OT}BaAܰZIŃmJ ] a9&<A 5jr7@nNDou@ иӍ,Ij^(:ޗ&PFjD5[cm",-2؞U2YEu |bP狳'?`xҚoF|BL?* JJ=f|]M_"% 2ݕ#4ogf+qrP N,?;E}O4T ϱw_]清i^%,&CD'$vd)cIDh츣 L>RuIZZ'V~.f |2 ^vy$4WԢi+ suD'2.44m6#>}vg,ف`M&0usLg˒ypB$🐫7eJYOB7(On`?dUFtR{$%r{Fg]6%LĴ ۺ&s׿H5%nA4/m/B)Mj‡U5=裂Ar бv{"~5ު Sh=k]];BOaap$K[sV7I9^ҴaLQ[MPDa!&rܟJEriIƍ #Q2&nIMV]JR;Zըg1ݞSI ĹJh۞6"Uɛn`Ttȋ,쌻-x 7󐅞Oa%A,o#C~J-a>}Kߒ!9oNyp.x:~i[Ltl\weHS[/.gˎdCzCA2DT{$hGd$(*뜸Qlɘ5x΢%PЊ|̅kiAf)$\߰mmB %E=CO7X%:/ứ4h"q֞d.I/"%NW,xC|Nq5Ÿ.!Yz&"yJ,JlU /_ -`g5 1>_&-z$˦` ^U4`Ggq?j2B=<;[ 2JPgߟ|؏Z%ͬ0/̢KB!أP*Q%ߔT _1+>T[^xW//}Ve|/QiϽ!JP竿Io~6i!~ j!T>f/ç9ኄZ2SXk21\}:^q]-//O%M]:FX:*c-EGL6gh#,ˎ_@F{a@xb d$57/b#{(r,[N XRemX~'$h!LWw!Lsb/?85S5|:Rfv&3wdS07ȣ|$HIOsQ@Q,s!m9{j锭)}:T}_;0J>l7k0[]/Y,",y딀L"Ҝ̣Rz0&)t}a~8PuOb:-DYet*8RT9lVMBKT oj'n0) jJ =@t+3}?o(f/5m5&w?ֳX.'.[sV[fڑLWHF7ހ&?~]<' 0f͹"^)NiXun8"xHgK]s FъU0}رZE?. ?fDͷ[U:jV!^_QzO0iPM&%[lfK9`ij̋^A`9 endstream endobj 76 0 obj << /Length1 2470 /Length2 19335 /Length3 0 /Length 20796 /Filter /FlateDecode >> stream xڴeT!C4и$  y[,h]NU)Pޕ ` jb 2223SP9M\A&@^@-OߜS/D6 (;2 { [3w 6ߕ~g2dMl<\l@{s,#@P;LV& :P&RUPVa|+\5"&=@JCMOuKzw ":,L`]@3jov5P[:21yxx0Z2:8[2:O pp}:m fo& 2ڻ'I:{-_boBiw86V&.++L@@{{@WW7_o917g=roPux[ǿw]@..W,@]~/:3(8cWz"o `ee0 ktrupbX;x {sߺ92i؃܀2fc@'̊wf巙M?GG d|q1q\݀~>t/g\U]\7S3j`o0Z3):?_$lmMRa&v [ W7UjEg;@. O2/6I``agdfdۣDپ}{9{J3{ /M~ۀߤLrt$AVN<0rp|Xަ״\Rn~ g;`mqD . `xLE\&?$ 6`޸Ao\7. "[?譃VEz[ϛz`E4{;Y0|SF⿐㍠ o!o t|cnFdy#j_VyG?}+`vS䭿#)@mQNv-˛:sot!G[?,8U?V{[V-Xߴ7 ~fsm :;@o#Dv߾4.#[TӇo?fo'zזB3?KϖCQ0Ub j&AevJZ)Ky ӂ˴)B1m_RfUL%D&55V˻Iiet>g'4&Nx:{X_QꗷoBy.`8ۢy_s}}H5Y]0. ǚvyKQ}f 'QiL>49\ Ŏ-irL)ބN[\J}m-Vj SV=I+a%,qyx\F!6\{gBGdXaCFHlμ$z?9q\WbnG_|#; zҕ5³E2u;^ Cxp7&;PTcؗ3>wHi4m}TEd<ܘߐCB4 y$ZRY`U qp˝MSj1Fd9Epʊ*SE ):t;9}/v/TXTb^., D)'&kt̋@˄g`L31]֗^@JVhQA29LSPrgY QYFL|Б,:f"`vAENsxJj>Vxpc:ETi49=r/ř}ş w4pU)ټ=V?"30(*KS(S'_?U=+i1ʺ+*&'&DfcЇ79Gy-|q;1V:ʩ%13 =W: =C6XR3 £F23MMk)j ~FJ:6QMa vz<'/*)BCcl겷 5$Zld}Sjw2?TJr@[ZW-d(9pIL4[חW0HUgƸ?A\ocAx 7K}uQ'/k09O&24>^:"_潛d *1H1rb/Z}&6݆* lx 87_J>^)W;y0$Y~PΡV)DA -]`ID1bMm.&:eKS܈LzC U0!(/dP&ATҗKwyGM9a L%L⣆$Ė.jGS]F&Y5C">Cu.%]i;/ 5o\G6%s}P@C1pҬ/iwAs5xTRdځ,,ѱTĩwx~F 8{}a0K9j[T#D Tj_6Fi@@eYewpba,wP#3GB}~wO_kΣEg9 /W@z3 \/ah'$PlK/F|m\CZK*;R[qwz86qt% LSl dso [uh'6=Φ^"oBގ \RဘџƩ$ ӫ[uIn`=, Z+rC;uT$6Tw;RR~^a.Jp_UR.^LI8x#/t\dkwT'=vΎяHg],*WH"%Y#>M,&p4!+5V{J&wukg%~ͨiUfݦLV&_"?#GUȇ4'qrkSd"2mW a\[82QmCY>ἽuɣBCpHf~0D'Ʉ /gİ9Fgsk:.>H8/]y=#Lu D~3к<ȧT}) $^jbf*8;J=;vkȳoUĀixg'FRwQrGiT%L(pP˼7š)V ^rWhr|NU0 =Ql5=<%h4n!Ju:Itu30vy*MnFc:F¥ze]G`.J]Dz *(paafS92oie3F ${>鹉ac_xk{C*7D &T4LVkl^r~ׁ8Mf5VhEºlV3)B' -hrxs66Yk9LK IkmFָ"sp.!*.R KIW>Guuf R&|%ǗB[Q9.ygݢ3{f|+ϩ.:s6Cýޡ1a6k<wpFt q:o~PT&^[rvrY0qj^d/x->::9Ģ{7/T K7 h <-%E|] Asp[>a뺸W|N~Qeb>S,MרQ wnB99@w͵O4^:OV._[" i 8styr`A[x YSIrsi ?ÄV?;rJ݄ WnXX.Hlbvb4M=C#-{G?{LϖumP3C>b@| ES'oo Bw/ ؤ >ь! !iX.!8-IkW_B3av=0O>h}( KngT!sׂz17l;dz5 [/0<-fzЭ CPϠT6d|@-!$fmZЏ6nze*.q} BݳGfк ?.h!?oq[L/RJ~T`O׽g|ݰŘnhWsQ?@S7bFB~Ah~ۏ㴧9+xTRʰt=J ق1{¯M]X[(j7;:CmvP3o6-qbC\G\\i&$Nzt5xh\\jp<wHk>}_>WxHQlj$l=- s+"ܖ4ī>C֛e!6@=  \܍j-?$>IS0c[u\(>ց㉛J#dm?g%M)J*†靐'B<+j"xP@+uv#4ʎ 82I1 *MXNX pNH?]JD^t]IbD"s~fA jDA|ukF1th=֤vH \%iFWɸ4o#yU@aƮzrlvW4|D^w>Q>z-b{&5\=Puo[~(іVk}NPb,gOYKGR 8OȗJO^J*i P*74SGr^?{~elnT>6AM >8=qRb,*-$p.T"+s C㦡q/Hhf&y0:9vORھ*m 'XS7"K =Z,ʥ`L7M:jTClk(Mc8Q;{KWZ5?P<'a-AFC cttkQn"7__<6M"ȧ}6c>Z[_ |$,Q-cd ?Ň&sUy_yrȥL_bKO??b[W|0<؄aM\k. |JrdhPVu##MTݬ2-Wyjȥ")tHvTLjxR@tMgK/q9OPU:K$Ir*=fVli)Ǎbn {Ϻ !L~E-A|Ν,zxTtV1"'c_j{)OӜP;rYwOD`+OހӱwCI<ʊJ`- ׅ(uP'+*VЯV5 )<‡N!y9gI9@V݁;I&Tq%v~1U(03 2r3"\C"I<ZA)R(z '\Z0TecI6_1>nqQ"Q\)iCbό%;:b%Jf>-Xxm2}Vͻ?eVUWg3ع π1( hW/Hf Y.qq)P+vi4 H)AdVqǺ3M=a 3M|ГlCHxxs>We^_%P2>^$8K-Uȃ+^,2`9Xi%`ǻ+CQ+ bK_jX=]INc. IT|>%I\k24Vwl,7Jgt}~,o,ibyr" 0@qf놁T4䈤80Mu1֡ V(dEG`+y" 4 m)4!$/+VdJWa!煰S DBa٦% DTio&tzUvpœs/ c k ٮ@F,Yl|3HUF88$ϵ94dN+, ïzd;5ı" r ƓP t(ZVǞFD| +~G*mu_v Yjx8+HKwtvPfLjܦ.úSDx ;UM}@&gllάϢLOLWOġ@Z6VSXܑCv6@S՝ G0ώ0(`ĺ# i2ϰCMZ<;0qԭ5#X֙fs-۠Zvf^nkw&߽7Bm3q\AZ;8ņO+daY?Z8BGQׄ2|5 rAm`Џ=,|\E@J+"tOD#6HXMx23s{Fq'ތ-_ 0mf4O93CM8QW\8כ IZX+f/g1.+ejJda<\ Dg!ɦ4k6:ͫEoJ4|ߴFun[84zTĂ/>kbhadN28%lՠfJ{M vcd Y5 S~_~xV${L*P:^Y1GfLn4\0UF6y\%Ãi i(8883ΊML^iY^1Oא㐡iCVw|Y۫Pc-*ՍrdtL;0YcM~2/#Qc7j3>hSz"V$Mjr6c+Ϟq'/-ɪ;_l mP.SvEe\O<<[gH~=xvo?\=Ak>@o CyΓt ߟQkL>PGHiTtn#@rq tl VuQS낏ۻJfI–j&mUFBa^ i$|*Ԍ'LvJڐn4qݒk_DjO"\Tm=cshB#_tC X1cX?8>X9?Ξxw(mw $BFUsDfEaO7j]8H$|/j;5xuj[͹8EN72Ӑ$c7$p8tz8Htj4]0Cw)Uni }5ҵUN*%س9_zu:_y,EeLVύO1@^,XhBpoX]*xcx?nLS-.f0viدSf {X˿)YaXk.Pw23UBeZw>s)/櫺ٹw)-D-=D&:]9J +ܐ$M g´K*"=aG.Q'z%@Le+%f\oFfF7\uw ! QI%j~)׻A̫HƵ`TtU:R*|&x ~@:H,@0:N4ƙÄMҶOsjkjlHcNt\|YT`5,ymR *˹&l9o.1@\Xx|;*9:u)bǾq{IE)GE2o`F7!KNT UܵYWG< #%kal4dEy0QΎQ̟w&kBd qFDJGvRJr~E3>%֏sOa?뮏-P0Gh$#*]lވ?ClfHNy{is4^YXC e/`D3 j]7 Pd.q~<,M.oKtP9cQʼnq6VjlpakV*Y%ON(i꼑cSx$VKKኴZ!t#bHx&$;7mt!.TU{ %Y0<փ&z (VuэhWCaiͯ.Zʽq fԙz`l ZlHγ O<ФOilZu}\TonL ʴQ[u{U76n+=tM]r'o{NQ]G>ZF$/Rޓ\9d84­3,g")x/0ʤh 0Zkc+͕,^F\$㶾!taJ+0ՙNKb!ˆ3n/Kx(Øel E&}u)lIb{c)!MMq84-RDxr?G@ґtW3hGS6tؙwFM:>da_#n z0=C$b m$~G*$tZ/%+cthGtv&d)fQDG:E"|Df=ϏÆS|=p㑲6[8Jv,GH0 HV !"WOf qLRF6S|)\ϭ8I+^4x,>gIyҕE.yEx4TU-vmc <Ҫ _7J S ɞF0<Vɶ҂ qXXȾ#OԄ$s]|Ⱦ}uʢʿ ~aG@XLGBᷪq}^#A3<߿:}Z1,dVHѯ@WxJELZ4/N+j兌HN<3ihfaEݭ041G鿒C_h\蔣]M9&35C8o+p( Λ#^x&E; !b'u{֝*-+8!ϐEFGiXR!4]ܑg:P;ÚUkuUD@QxP3ge b/f4yx㜳erzǢל|:IugB]YXza|vz55V!>](eu`O^_KOsD䈶9S=LT[mg1տ5i9>D|Pt33a3&Sd [Zuz,A>\5R ү`#8|A5XEցHGhiSCzhDi7 kSg=C, &/Mu%ص"ƌ펣AP^ @f\ގ߃?3Ps֢&C7Ap"! y^v2^#`gRi1~ZNCpIs_!WH{3Eߤ6..1ZÄ?E9;MiaAMa)JmQޑMS)2G;5L@C!ovn}VNVOqrsʀ*Nrdyh.Sk:6բTx#Q'*_&K57HY&n0w ٱ^0)~|fԜ J |J[!(Aέ`~5SW(>o4[ , .+{NŬq0YLi2;vܭY U>x5ol3Od=`C NӖzJ,tz1j^J/٢ĠvCXIV~TZ̅] A4oP}mWB?a48Td5Nv3a/6C[O%'x"$UV:!r#'9 Cƹ [rdoy,>H rT G$ca9Aau] QSN])>dye0I. HWpO~*vBbtt71:/s%`k#u=Rg96ńOP'ePD2\@ f~p! +RjJ&J*DhG7B(tz|pwSA ItEC1'a=Nu!E = P%VZ$ J4v+pN9|&V;Ft&/(!K9h@D֌i-b>w1-e ])7Ծ͖kH0C#mW <'JPB qN1$6004Bش_^?M~=ߣrit*&s|79jփҚAIN(E}iM^0*wYYI$vi TQ/XK. `^;8fZTpzfЯca=SK-r1AkLfOהHkR-orp 2-}u߬| q\<#|iqmqxF/Aw~}&5`2 c)q~=iiΘ̇R~c ō}G dzMlCeOwW0ޑ$i\ j s$ KKھmyU 5ԫXK33|"˥c/(qb[iD@JO /Bpf|(_Ht?"Ϫ~Tt Monޚj< fʌM{w]B9_,-ɀ=נ O =zRB>0V8wedLHCp-e]տo9Wy)t'z\k@M!*MIxbZsfv ',q;-Uu6X%S0-ޫ6._CzCO)Rv)ޗM6cm-C^ ݌OREmΟjGf+\NC1mw@B(f1s5G@k ek_{R@;Ld('0)1Nv=p V҆0' Аƒ ꥮU;h&]zwRL눉#q&35k!u =^Nf(d>Z +Qĕ7-LU>׊4%07>`b+6K$k`feF-:+gi8W ?^Ӳz54${5s61QE3a*a/500|O!AWloapK䟧iCOB0`SpN2Ӱ=Ԑ oCw ?^Gayt4v<PkCj>_-]M𷕍Gw6C_Cvc4dZ3L9_V,jЖ!B1i@44LŁZThwd9qm& 94@91Zx_A1kO4p<M!x٨y]I~w8H`vN}$p0g[GuHj0vCDL!_X]FU[\Pӆu*fQBUboI ZISCJC UMid+S?yO$Rq5Ic̾@}:FxL&ayؿ9v MyK IeɡrafpMl¡;9%YYw- a';s*$0Nj 킅6v\.h/_{aΡSCmi(NwYm3a@ӡϱu#.=2e=r;tta}Q?~b;P {K/h0yD RL|=ɤ?_*'_!Y ^'[*UUlYX/0&k;L|l;I$.u.Qӣ)~T++{5 y,t= aޏ טy[]1wQ! Js!lcZS6-~&t(Ly6=3?˹ #f/;0$5"I2FFȐ9>~Q\b^ 24ײ 4WuAF:0y+00j\G KpODh7/&;ȖYRʓr Cƕ 9sab6m\3hmb^@Ъ.0,5SK4n4D !_PUofȓ.Uʚ?NJ 8΀W3(E{@}8伈x=şx,!S{bU MSg"|U聊d?a'g [y}w7X,l4^38r6 n%]*`bCyЯ ҋ״.[(=} K}HLQ Q-uWqF_( |ڊ~͞=u%!룮a&Wulm<^zSt|6g4 cDr#V(⑫Zy` _V=Vi~Vn]M73:@q0'%vnaR\"m}uͯ.װrf Sn#[z?Cn*PȒ73ؤ0֠^~t)Yvsq٤do8kkb$]b뀘0f N{oXEҠ§H&:uZ31|ԩ?ho.A-&2b^HtfI2ả6e/ܮٴ}+/Ɇ,I3&V*PU>qJKhP0(f@#',ߟ;c,$}jcqkoul bu~$WW)hYPA3)ǬFil+h`J/kq\Rrv-msfƧQ&jX24\ ڥ:ql CKk6VOj/)QlCo`pEouhZTyJ7X6Ke_3CWs,9[bW*fp< Ç(}{r87zbexGЍOKݛMEuXV2iW*d>4]mpI19ees6etC_Uikgωx ~|vc,,RnL+ډlHe"[IAR(>xWԷ##9.Q"D,)a JcLէ?;¢WQ7sy^xɵ f4X{e3K@RͭMDi@\x6OmE4E@$ylKJ~Zlʾ endstream endobj 78 0 obj << /Length 844 /Filter /FlateDecode >> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qDŽ r Gէn8A{,쏘LEvDB``B9zK~;_q`>Wgy o.>ݫﭯAbZ%?6G_Nzy;9ڰoiܰ^]0zu\~3ݍܥ: ل0%1 " 0Z{q́0R0r0QK5<T`,if,1gT Hӆp1X:,p8}u 8alSM3?r>x\i"EܰpJMkl4\?ǚc:#?^YHwuprQF^odž1BЖEQ?1^׆ƨАԗ039+ãbLi~jЙ}s~zrCOe fYJ|֟uМ8gΈrY׆}ŊϘъ1LҊkgigϘ݊og3f3|3ߊY[3 =L3f/gd ,' f)Rx jb&'W *.MGZN(:p~7a?}]TyԟE}Ư%Vu'e% endstream endobj 79 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3FXҝA5(O)suߖcHQIܮQW Lڮ9ˊ6nK5NoغWi~r<d(Vu;_=85vѩֆu5CNmm悥+U=#)\][|, MHS"#p #>y| #:##0)%T\`YQqJƚ`ci|1Mَbo4m `2WQ/cW888sέ-./qJ;&\ k(d?F#h0\?Ipa]~9Vk?q1Bx.BzҬÀhƘ'g 2xk=6u2,bق6E0F,eL燆LY` YecODV3Μ蛳;zr֟P.O0{S3ux9(uF: }6,V|ƌV|gegV|F_+>O+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z endstream endobj 81 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z6 endstream endobj 82 0 obj << /Length 843 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N7R!ݪ70W?g_,ɝиYs{ ]7;׺v=ߩǡoݨM'opiT}IAu~\3;he?<{Q%(SVk-#&9sQ擾ݾk^!00j(+m$?Gwt>X.oTuþ{S_tpСtZ|I1?H/'BZV;ݛ ZԲW/{FR^ww?U4H6!L@@B@q\s *G|F/+>㹴3Z~Z83f3[:٭ ߬Lg3t33 ~!>CO!>S 33>IY ?BXIAup*Çq G潪N$p|eO_:q;:'dE_kCa endstream endobj 83 0 obj << /Length 845 /Filter /FlateDecode >> stream xuUMo@+H.ȲrhQի C}͌6jo73o{q3fѭVO4cpuU sk/wOwquy_t}??p]AAu~\33cA}P>>%t;en>r8`S0Aj~vUk&Yos yv rOiHM0[7v,ܜǽJnkz~lNͿvt*amкq۸qۿ`J-ztH]{O|, MHS"#p #>y| #:##0)%T\`YQqJƚ`c2U{;5Ҵ!\,18"\aD E_sN[sS9)9^W$js7 GZ ׏p$uX}/S/w"': fyRy(#c^g!ch"ƨ-kC^d cRx~h K^| МQV14Nd5cY9Y?C9돡'g ?%>O:ShYggΈrYgDg>[bghX|&^V|{ig33qgng3tZ[Yog,g-g B|B|\3gg3?f)O5[TT+&GUP#a#7q/c?z~#袳rdbP)n endstream endobj 84 0 obj << /Length 700 /Filter /FlateDecode >> stream xuTMo0+J!m0U !mTto4j{zv|tv ںQf|6'op݅uM{}ugfci"Amƃ}>,%rtPRJ(:X'Ab~oںT7h uSӌ]Acq`sy̟M.n? D`އщ7+d~4Wj7vw VRŪ,ׁk/bxO0+,F )1!Pp #]QxQTv)#ZBYLt/X^r<1u%pr_d9٢PSi0@WQ_Uh֩h諵"qFM]RrCpt39Âж~j3Fezp888Q:1bc7~}Hq('bĄ>^m# &zd}4)` "H,4%!%AQ߄B[B~)ҙ́ _)M?DM;豬;kyoQnNRd\Ӎ;WA} zoZZgbT$Z|U endstream endobj 91 0 obj << /Producer (pdfTeX-1.40.24) /Author(\376\377\000J\000u\000l\000i\000a\000n\000\040\000Q\000.\000\040\000Z\000h\000o\000u)/Title(\376\377\000S\000h\000a\000z\000a\000m\000:\000\040\000S\000i\000m\000u\000l\000a\000t\000i\000n\000g\000\040\000s\000e\000q\000u\000e\000n\000c\000e\000\040\000m\000u\000t\000a\000t\000i\000o\000n\000s)/Subject()/Creator(\376\377\000L\000a\000T\000e\000X\000\040\000v\000i\000a\000\040\000p\000a\000n\000d\000o\000c)/Keywords() /CreationDate (D:20231002185741+02'00') /ModDate (D:20231002185741+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/CVE-2023-32700 patched) kpathsea version 6.3.4) >> endobj 2 0 obj << /Type /ObjStm /N 67 /First 511 /Length 2949 /Filter /FlateDecode >> stream xZms6_Hx'Ng;n8oۤuhItH*MY(Kr׹vƆb.HLKf4Y`iYʌL*fc1,D%BfN9)S 3%OAT,Ր2)24 4i g`ڸid!59_J( RX U2"X"`|H R-C>ejZL_XUrl"1NNB)Jsis ((?)s &id$Sxp[ %w+Ox Tj7  F0Rcgi;:/љ.0]yZ2~49g~?*gU6\4#?rYI=.?3yҥ|ځm(! QGP6F <f À?.I^ϟ3Hqμir#(,n g϶~TcQ.1R{dU iF!5JFH}5^y/`κG`..o@<v. wW"FM#DK0'EZF)z)b֝o)E}#l TUUyxiَUf;p1Q= r9EmaX;٬aځMnF R :0kr_BIo{VNv -'*,5{l^h{$ަi]/(E66l<y=<)b,XLijM:O}'yoo.͇EÚϋŲ ?.& gu]i}y6^69x9yS&9g\UtcO Q5a| fYG\.Yϲei?qFlohzwR~G:oM%uy:"&f͵٘57ŬŬ~P,_ve+_o]Fܺozv ݯB7TT̩XR1""Wa&WdT,dzg_ yiwllma#3:??9<?yHcmG-&J,؅7{X ~[qx?Jے86wS}UOVFjEQxIjGň.qx3rO'r3$;Qte--mz|Zć)T7D mk}ZWt-pܑCԌ!h@a"\G?'<@CQŒ! e}V:hIT{u^AOLK!%Xe4AUbrv_IXhMTFJ]˱Ʀ:Z%P^9 H|EIR!$QK.$N+?8B2INAVK[M:T!=:Q^V5l]I`SߚX5$hK t5@0lv\M#$ecmpekLC41R4M׌T"6)@`PHOwS+1N[ѶRCH_A0B;f(SKIܧXD=`k)z"5"ƈut<-&e $J4ўXoK|(v㪸Q-nA__pjM0˦Ga ?4x|J3vtXӱ >/# HmCi͊b?!zSFNJ!2h7fĥt5PQ#>N<( tx$EO#7H]H>1b! ҏazON> x^.;J6Ԓ1B&km3\)Yxȟ  CzB%Eҳ)bRQCtdcøI99(lF)}[2Tpb9Wu1_r_͖5&gmeq=C~rӵߣwv=m&>+.ix9nڛWᆰhwa-߰T.Xa"7Q!"mhFjɌQMmifi60/WiLa#ARz53zd^D貝a*e!u.2__ڭmׯGEU7 q>HŤq,ݵm?. llTMUU*VUݭjpUoّl䶪Bܩ_-Öz[U7PUkUݪʇ{nk͆v[SpMw.J[;]w+Ԝ|וVm]kް^-i=gŤ^}P~оkm__7;[e3+$(JISCCu=I~OAZJɷ^U͞)oYI4Yhգ+0{}O~{q7_gǬb^4[Cη@,hD# oafC6X胸[낶X5l{ݖ!}%-35ƕjxwJva('_p]JpOWq4͟#auWb?@i?К endstream endobj 92 0 obj << /Type /XRef /Index [0 93] /Size 93 /W [1 3 1] /Root 90 0 R /Info 91 0 R /ID [<119FB5768C259AEDBA03E05C6A1C4723> <119FB5768C259AEDBA03E05C6A1C4723>] /Length 258 /Filter /FlateDecode >> stream x%IJCQS6vIl7.67 : p .dΜu/'CqJo$EX$ eR[Ͱ bJ]Vh3s>@ڔ B't 2^)@~ }o*}vMs`*0 c PIafMɧ3~x7y*zZ0U=-Z$, psoI't2$ {=?;!q endstream endobj startxref 177994 %%EOF shazam/inst/doc/Mutation-Vignette.pdf0000644000176200001440000056260714506573204017363 0ustar liggesusers%PDF-1.5 % 9 0 obj << /Length 1923 /Filter /FlateDecode >> stream xYm6 _at.6n}[n+ $F;v_?Rw6K,R#(Rac޳{<@PyKOp<( $ {\zLylꓙd׵.;O`~xd$HN׵tǮDCvUkjPofU<R`B81A"DU UdD >S<`19@z^* e)7G7۪s݃ @̐/4P#罙q%R\͸ BڍUtoMޯMC 'fܯOD;huS-,lOC7O]JP(_`/62RC~]Vݢ-VW^u^zS IgeN,.heHMʎh*޺xGcc@IU_#b08VfBЂS1 -  8%BA4A~z3Vا\` /^ }I8 'N EW罓_Z<.rWg898<#[J[I'#{$5m՗ o֒؍> mScm>AYf LCȍMW2P"akkj,n&b{ 94fG۾G8<: v4!-ΊMA\be!'U~" Ren< t)1xvWHCG q>Ɗ/CvRHϐ%$ Em Bn19ICszbeq`mtD,HJ":،tnh,/ m*Tc& 13>LZZVםqQlAQ˂d\B\NS3Mjve y2)y n;B |Ny[Pc5Rdh=~g'a•OWfTTNjQ0>g,t#Lkfi|s2/24# *#fMpW>Х[tëQC/;{4q-"%@ "7y+8L_B3z.tUj3w[pUG ^ݽ#@& "?˙bG&]5qsG&(CJ;](8rwT{d%oƐ} Е`$BE70H zAg tpաZ"FQwqhc_>> stream xXko6_!d( §C- `C7%;p%n}/Iɖ9s[|1)R<W7ҧY/V 4P5 0ś oecZ6Uu0L [5c{bQ (2B y:?X_/CC ыp0Н2Lƈ~.YHjIGi[z?C@ATy.:M8Lnox0pB.dkYtIVnO2fSUH318fxUSeAk,>}Hmre{kd6 apf@0`bkm%q[#kB =H 25"ac,M´<{/lCL\+o YZ;ҒWpu3)ŚI vZ~xo endstream endobj 34 0 obj << /Length 1148 /Filter /FlateDecode >> stream xڽW{o6ߟBP@"<fqS&.aYfl z<~GEvx- wSGdL d zo FTr;ή>6ġ>5w Y̪2yi=|O<'A+kXc)Cߗ4 8 .8AgcxM'gM˅LMyiY}žY2)y^p 5~x#!Ek[JsыCDHêQ+!~=gK-E\jƢt|= qUhm<瑰6w<}MIMY<ޣI"Y\5P*t4/ra)"o/`/7CIJ FT2}KeN7(ǟ!kh#oqS;|㾻r{;ұK|vE]Ap@th ,Rj&q!'5-jjkR,lrI<Ζ R@fQ*V•Z[$0 糬=YVrV3F(JE'm#+VQ|ns2i,%ue>Ō[/7%$QgB,T6,>Ͳ[u>e8s<]nAQPudax"'ò3MD _XP(V3v ."y;_~ϠCұH$d{l9&oP`>,0K~(c+B6<,-QOmepWko-d`# vAY'yk }?hֳfePe-isχo> /ExtGState << >>/ColorSpace << /sRGB 39 0 R >>>> /Length 2465 /Filter /FlateDecode >> stream xZˎ߯RZed$8@00 0#ŖbXl]I3$FwUX6 2{NEЉQA Lsd=>DЩ֋SD /hm%CW;+هv0vzER%4lg Ve }[.GM#Q\Dϸ޹[۸޶d-KrťLQ\Dϸ޸TuC hؑ^ pwتW 3X7)Yξ)~hWPH]8DX(Ֆ䏾la7JZ;)Bpp'r=a[eRk[\Pڸѳ}璼qMKX\Pڸѳ-KrG["Fr=b[40X-vPٖ%Mc]: CkMìm-H O\̚Kqif%T[1iv[;徏1kvﶥ>Ff1cI ز-x겅xX IZ{N9M kyIYW29ؑN9o&$E4bgLU-Ks&,deZBv$W4]%WlZBv)9=`8YYɉy6 fYg[raVv0kZܧ14M:?ء%OjޒJ_M҈Җ4^OO\ng)t^qaG[EA6τ\}sݬה71wa[(#~>H`b[F>֖K\۱ąm->ngW3 u$y\ڪ"/>wmqi&s`izk﫾*UXDs]Iv=bDs]HoU$q=GMxVƭ\VkqO0UèV`#%:q/EHn/)yLowܓKU+. rĞdv|4 'u$/|(1NPk+Sha3XŎ=Wx>$vxr$^\I$-!;‡nE\Ap$` L^C<UNDN2X7so7?Ķi:%CARlEN{ s%CD􂫘P'.-nfh++ip/ V\~qKs`2?VF+#-d;lGE'bÀ,sTa4LTB"@DE&*xEE,&*R3Q+*R3Q1,_Ӝ?F^{3-X3eX뒯k,lWTǼq)36Y+rC=޹$;`}VVAG"zʶ"%ɟMZ 3UAgl7'.0OL1OI>6.gd+޸6l̾Qڸ^pzF *dTJ꾊6rڙH̋!׌8"Fu2=TgY>גb)a`ɔ_rkߓu@h=`n^7"1$dp7w~G8|"0bQ۲tuk`>3|B^$@ɾi6-7нP4c lJxI.a>e {Wtm/HG6YvM[{2ss{]K9AG r^L{|m}Ⱦ6C."_`jT0dy߉|i9 8Go<. ݺ;=<,˿1{ endstream endobj 41 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 44 0 obj << /Length 2592 /Filter /FlateDecode >> stream xڽZoFBHQX G.!qФKh-{#%_ߙ%ť)Q{\rٙ<ųBF^FW#z|xIX~q.8:eTS\sHhpzM*:g;HɝqȝY-I?;Ë 1/pPTxI˶ B,t^[:p6VU91^oHO0`FxUm"!q)WFsX)3B')o䎨iEϖsܦ.oqmeGN㡀%G4G?IѯCBo 26̊MK"``-*F$ 6KKvf %8$DsXQtn : IeIw`]ȉ$װea'cxV41QUfDT\+3uK6 =ldim%s|?tE$۱ՄtX`cp%riЎp}&ss+g^gQ^ 3k/ӷ?Otӧ^DE_x TEz'q`0^bI^EwaWIZlE20}&S |Wi:;:98kd•m}L.ᔓܤ[=֔#mZuXykչ&٤ybxy}i<,S<# 7 L 0XҾz? xɏT$xU:o ![MZUaXёɸNkD}"e%Nxaw@gZ*A$H6]W,,F򆎎q1n,,2pG%$20|oTl+3+rOΔp9W52#t]gV/ $rkcA>!5 L-24 $m3fGe8ߨCGj5F%}e^g:h.<}XlFņ3R,8#M HJ } @":1݊& .J:pP0ޅm?p6nO= ܔy ,+" 55~ӕ8^seafx7zMLf.YWBEQJ2 zb&wW+O.Q {ƦXuOWλ8Qâ_{9cٸ߰5žǺ_A\.hƇgU׳j݌gF/e55{)hdEGh1&O'n|l|eQ6+A\R&\[[LS旦µ|bZ F̯;IbaEq~#}y*̒";=*fl S4}<Խӎ&VK oj(O 2qqI{xܢ"r6CSe060] O>}Lp^{ H ۣgr endstream endobj 49 0 obj << /Length 1126 /Filter /FlateDecode >> stream xYmoH_\UºB765iTMk%n{ aMPpβm0vm>4_Xv< Isۑ4Q_F%Ud̗OtKd1|%)w8 X2sddnʅ/1=yִ [Yw#!gK_JoUr5ꎽJItS0瘧z3sAAJJcߜOJF9]z8&)6#! ]2P 2?vq M.UH@u RlI-FHOv$%_ A*i=V=V EI* *F.a3?tٚhNI#ƭhKF8چm-< `Egd!s:`~:|2l62R@U$+kq*4MFW'tE)4N&JcĈr}]L?L$)O"d]Ӆ3U˛2І~;,i0iC[Յ4J\ZvCE]t4G@ j'p _|w|x]Y<@t]CH.i,Z*&V7j{nғ7-$ Ŵ-Ǧ Z5> [o^}񯿝s@zln@ h^X!X*65gQvޘm{Auڃg.vx~n[ڷ!M2 Cl0!SFS23Hx $u.|eْ, J$يL)HT5b3YD*Wi5mA0eF١SmJ[kG<Ô5ޯUrx -:jj<'w@j'q`ÜWC΢*o^6VJk"K)iCK[&͍fgKc+KYwG\v4-)aں#99wa뀓$(x-[IJc3 @vr*٪Ua䪵oZ%1_8+uRnH. { =ÏS/m@U 3GGz endstream endobj 53 0 obj << /Length 1365 /Filter /FlateDecode >> stream xڵWOF=Eu#e; 8BZU$nl8w&pcB.W oҲM~;C0pD$6&(4 3(!UY%n2!qsQ m:/=LnDv#JnDQFv&=V @~i~ejļ.+-Bq60Rϒ5QEӤ:V4-'H ]S{1OTJK1E!be##adnY'JUBE*X>էb^y=|>0ºNecT {l=L˖ǐ ~ؕ8RP[&I䡀Q[F:̓45jk 2BȢa@*p@.YK|z:7 @P(5*,h[l%sVl1/kZC7c[2ϥD[v~k19k[#>4j㘤8heHU6]~vdwmܯݷG.OדoW oQW+@!H:o&0JJo[EA۴'=`(1MY3^ש̓THM_֟:xQgmFm>N8YG6*L3\PҾ hI t1"Kry kt3y=Y46cK=W 䝙6*LJ:)G>)4Ti8lZi4aE _gs>)cX6l$}idk,FlU)^7> /ExtGState << >>/ColorSpace << /sRGB 58 0 R >>>> /Length 1887 /Filter /FlateDecode >> stream xn7>_cr4:mHzr `<ߗII;wD9x oQҌH/J}Yٟ٠aPyvg3g(^J=h> y5\z@䘄rB{/S&$9M @g%m. PxR2iq21\IF`L^53tkvt.uF0vw;{J7l0ɮY[ 6%M z{mG=]=B$aR60ʨD2iaQ)pIbtPR8xKAR(d k iodp FAwxO _ xn&Wю =)fpFSPқiL pQ$*GX+-uwЋ>SCW|j=ʠW_ߞx*}Y}:=m\Oz<x\n}. g௚] y>] {sGu]zBn-Rajr8#pN*< ik0tHm2ਡk|W1pvl8o?_)ty AΗsR/=V ?+Ui:nhL |Xs.^gOۍ=]`Q]\8y xB7>]ՃHӅxtFcdFKܸQC޻F@pGa/(nF$6Y#\4"kӈpț~B[?3plJ5Zi`5GeFj&@uBމxe1pu*ހ1V}l%^Pv ,qyme#xy'v?^_಑k̎YQUēPb#zJM6B/'wbKߝ_GF|;> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 64 0 obj << /Length 729 /Filter /FlateDecode >> stream xŖ[O0+,xI4blvi *MQ-.Iݯ;)ek$|>7 @``!sZG~0! 1OB|2l/ vs?D9}f# Rz";߆r{Ѱ]]XlƢ~ .帾$Ev< % | )s 9\8yNBvwHЉc-feM%i=K!=s\l7q:. Pz>.&[z?fH:ZթŕQʈUk ;-d.r8 uezvZ˘oϧ+/ [EV +2JRɕD{֗\,p-V*\)rQd9Y_|E ݍrTGNͺXԫ#Ud\[؏{˄$J,2gf${ 6,{7#V$+7I6e0AODGzkK%Gn&m2:1[X`*1Dh Qֲ v\t̏x3+{~M:trK[+.fkZ4Cq! ";!f~6TN,9p\Vx) _F?1U陋7«0& ɭ 8۵v30ݓY:WBj=LJ D;>Pd endstream endobj 61 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Mutation-Vignette_files/figure-latex/unnamed-chunk-8-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 67 0 R /BBox [0 0 540 324] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 68 0 R>> /ExtGState << >>/ColorSpace << /sRGB 69 0 R >>>> /Length 1246 /Filter /FlateDecode >> stream xWKoG ﯘ}tޏcen @k99ԐrPߗrm҃Vojq%O %ʟ5⍸~<~3 (|&h.Fk??<^>l?}oX:OiШC@u3h\_wϥK0 <%X@yNXB2f+r`Z:8-9X6>> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 93 0 obj << /Length1 2010 /Length2 23108 /Length3 0 /Length 24292 /Filter /FlateDecode >> stream xڴzct۶vl۶m۩6;ttl۶N_z{5z&5ל뭪QdD*tB@q{;:&zFn3)L hbio'jpXL\>̌\pd Ci 0]T=LJ hgnipwt4pO?i#k{wgkK)@^ o!PF6f{3*P"PVPST`?\DTT$hBb:-@BMEϫ*9-@^C'χw91U!U-E1&?k0܀NΖ7fSp5s+Łݝمɜ/~w{'k h0vtϖd-Mv?N+m?J!w_bpos3i,UTYڹL> ]\\>@S "NNrKiE]ce6޾FcFv^.@ {?{fiLNH^J\LEN?cGxBNFv+ILEmm?X;)G\<[jfigjꦮ jv@)[ft0ßTu1G |fF6@_K3 pqrzS`jiWt);3{&RS5Tjjog 01ۻ|4\66F@,[ZxhP?--=.&WoG ٙ?v/ڟ9h؏Cϙc`/G/X5/ !#C_򗑘9`d lloV6z$z;{/ FsDF\ElE\FF?EL@&`okf5 `4l?b2}a#?G^ڸ~r``Ws5ՌnU\쭁8Yz0~$Ӈwzߧ?=X9t̜\&6?_濎s.@ R?2H2.r,~M̩v|ѯ;@,B{YIn=@d_0m6Z+&LwĄFՂ:YgZZ?FODڻ~0Oޤ떵}t/cjpAXB_uym+L=k5* ӅExK@y*٧s?\ԯӴᲑVTZnb+E5oy]՟i!T;OZ#0KQHB<I έ' \]LJTYq7ٮdՈ>ݧ`Zҗ{/Fl㟒GGX7FT"Y.0Z/4ȇtp]r}kQVkث0!- KJLO%} ÈlY)<偺6#a*jj܁ pTFdSc4DВnC-[9B5/q{ BvTJn(p`@dOȌ,Mkv`fQHE$Ğ4&cU 63>V8ڥ+Mhw Yŝ MjlpU^F g +;uVfO5ie0`D(GN FCT͑:ϴ\N\h_wIS-hwRRYdChNĭ,@S2ȭ"-7SDMWpo-x֐-9g5S sVw pD cM;kZվ On0Ϟ\J~>#xSz0ppq{ W D,W/l]up>+Dkv"^^|<6ffpN<݊۶JV*ưq\yL8^Ije#~{>H;? 6oH6|}P Y$_G´Ԗj໠$!>4)+u=jdtqQ3 EׅssPn_slq6(c삈d:z @B3|$H ^@ PeatoI]d&xC7L+YK4u*sUPv7*=M19 wk̖#N փcqN8M}ɞYР*<6vYr8NxA; $C }+UE$'yUaԞ8|,bp!դO>!R)>۽Y\_HاM:~?M@NhRFjX63<-kR9/}R&Gp긤]8qMf9vBe =ZIcs#[𒤁Le?X܏o06&+aHO,H+sq٪XO]@ҳ5!D[f̴C=`h˩ۃ V=^k)pDԀK5 .OrUViuyMP:DM CHtA:#Pٙ+Y6p4x+kUL6J]qgHf`<0wC"ɀժ3NЄ"osxLL| vjL  j{ Dpڍ|Ef*$eZ$e4ybL<[\lȈ`ءWMdL"씑J_lEBr(Hb-H'YGmtcT=/x 4/mb exL.$7ppNf= Z})7w:ЬVb@Ssdž U;p$+(R6A _˂fɀ|dF16z'ƣ9ѹ#Τ+C ٿRϰ@{UY0]CXÅ\:ŵe4ٯ.K[M`LIs}̷Tinڪ 7_dh@ %:ΐA2mՇzLCovg-ga|ue$X->sL&"ǵȬ_Uy3Kt!Ǩ0@8Q11zK5ˠyQ3J#P)Cq e"Í:+LKw7;p6bCC{C)3KBcmȈSɄ8r\zN,0|z~\_dNdI,HQZTHihŬrED&ϼ+[|xt|Sͬ7o\XPaʖS7~a8IBf\.TAbi[qr7O dHG[ p3@Ռy3@ygh6Qe8m g Ep7Ya:dBa4^3ГC;Lv^Qщ@`>;r)-Z!:g%}1H5~4JElAn{,nwei%{B=Vj"&X]wW|Ynsn G:بBNK?U*%ug"1xjpl*V:9E_g*V>yϓ4̍rfTKqoȬ[]SeO0 [\R+!FGxk_{gZ.*CmwCg RLzSzSk;g'M`Qwh0 ^05X!BZ9Mk K$oҍSUO}Ѡ|o)p>kٖq@i%v-ԞM&ٺF8QVzS!|kGGZ{#Pk!DOO1/C>@͇T9+V=pCv-Э\wt͠\pRu_,;ٻ1Xlm%u;3CTbSK! + LD}h7!$.ӹvߠ@)e5JXKjԴդ+xRjoQf&.D(嚚A4=ؗaLߟ:SSVZ1Hoˡ+ luh'U1]S5G2Wvl4p"4Ė<|،J~$2poWLpƬ<ϝ4o(7[#i&K6SD$wlq-?£]'Fn\ ܌3Kn1RoYfz2^&/\g*Bngē=K[=r P.}Z[{Me>UAC U?&YQE됛6"ki)BӪ?@h2= +|_W^Դl5=)YKMILl$;LOugqx&)|}pÄh4k473q1޴$mnc `J ao =fm?d㔂o̻RQ`^XJ =?QIFA=:tw#fm*Ո61N=yH?20t:[QHq ٜ+ļo_~Tv,Y5du٘,nbs%kX8hD8!5B;ϺK`{)Z|4YزY- (utE8 hfl}+ߴ*!M~єUbg>[w[CIGfL1վ,;< Xa>`2 $K{q7&~/گNP  * CcxNWi8_)݌C OɨD7$jj0e3x;azU!H|ӏU7oaÂ>(@u2H_ѿ̌XM4Eb0]!@փ Uۨ{~k؞Pl^<1aRhn% E8Hē[&aZɗt~fIۜ= LK6 /hSwwIꦄ0$ʭ.m$r@q//~LMfB6>9Xx͕cun->(khMVqu~ݬGb١R\>|`~k'D \*O!8at;~N-F]La8mّ 49oػ$}&sk.k3HygJn )=&LyQL6ѐr%wq! ;+4gctR0;Q$R U{sѝ&B"ɜT", *~5Yu߸h/7~1׺0UftB`U97dK\p5PՏ)3g1fuA~4nѝJl8.@9+[nKÑ|βGWH4- /YZ1n!Y8+ S%Secu-bUP3d?gEg`l/Qog,2ll b_HU R~g1T7$+pէn|ϲ՚dwݗo>svˏOܒYGx3ǔ9IJļq `Sr:c#%&9e#C?+itpa>RHpi 9j9)?"&]~`bY=ѝ.Zq4O>s'e7{'Թ^) Sxb,CX5[[,rN 2mzRBeaV<}ﻨ.ICȌhOB"Вƶ_-B'Ag+讻D/17v*If -^ gGXF1Y}7K~Md~[V7 5B0@`dW|^3PMf&Iϗou8{@ܯ[_'+WY*$Z [)frwTnGx<ۃ4CZ'9XuOľl~H6$5լA]TZZ amzyQ Dr*+te $.3 >D[U9}XzH(PSe!}X l +G(6s߁4 bGw9#: Hd%\7+gд %33(m2EWHN:e?L~AO;DUM˾rSUz9AYNRX@Bs49snb/dѓ u:Eb[5ܳRK#+tJ>w~$ͤdK͆rS$Y = 1(f( uq[Η&4.mdD.}d>_"7 n-Ȯ`j~ZӟcED$QdRg^f2Cwm,Kg`}րTacںj@ׂܲIqn+)|nlx- 퇙?AH}nZFh!F9 Z& CyKg;l(jo|_J)e3)/uG߃\OR~3 A3[F Vp\ê\B껐'ejo.}iU yCK`Sjmƽ $ߚZIXޝ O,>jdXEce*֨઱WZ BTf@OeE內g|q}mZ':*Flo/m5~o/'0rG["]Tghj Z+w0 {Gm'ZDb.b'NLQeDkWNIQʼnM@!@PtFvv=4,X%Ҝ<ҡ,b2qRdxق0>/bxn\rݱqr ؀aC/\hᛉ?tkN \@ 8t 4bhχ6\J^ to CL+1,  ?Wk5-j^k!&QA#9AǪSEAH ~e>Ԙc^rPRwlFa]ͷ{ Ő;VXm|k3`;3<^v?~\U+ʤHꤷť:=[\yjw%tWB'.?|tfNHGQjƂUv?M;uI/2OOMgw.ADPM3 5H0JzuNfMCZ lB"27 PJ;hpuz9Oc^CĜEҞe\uȶeSyv ]ydA ٗ׌Nϲ[B$3:j6i(~Fѯ8OH%RXƒ` 7֍~xG?_駨\yoRCvSLM*a Bw&7*wk2B핷ChڥIubiӄSԏoHB鰷ޝ1Wy t cD9ܙ?d2Ubȉi2]?C.pIゃ|ha'53dEOdozO7ؓVYzYM@JU1}Z%.:9*P Η!mguPlqһPHv`xGZ{Ä=fs&\И $rFO'v֦"!bW{U6^* g5ム9݄u Jc. u2 ib {7=?x"\+R- %j# ku Rת6ee J"ba3 1ݳY*VB P+}ޒ#GpIz$0fw4!%nra'xz٤TWAeJ#5@%^]`AeAkkˏ;~!>.m8$3D AH3CIq5PĄ倷)4SnU?bL̻b\,NEjplGON]{A<ӝ4 U\*W)!e@ڹv3BĻ"z*9}[ʦ./K<~eHl_ H'tSmىc8kQAkL^eKI+u5p\񝨢*3>-G}W4iZ;3ũFLkp n9}%XOj8@OJ VG2^/JLt55 Xj [$i"plI矏»=kMh' t1BaT.9bwD~JV^( FO) mNK+Dz1v,"%yLw<*dxu5],yl]Y gO/ M\2ЋO;5R c`T`fkiJ^-vnӼf44b&M;c%n;m'p7עG/8V]:3\ƁLlkkΧ H!E5Є0Yzo.w7JѮ`z9p*riQpscoKWȋ}0j,ty']5> VO>߱iP$j.!/㖾'xm's׾h"6_G'Ʒ!L1A-E-̣A!;HŶo)T]3-x'Ag<.Kuǯ^W% _-{cYKdsqCaK&i)vˏC>ˎn w v@BI8X(TuLP<EJͭ+[@7y`[X$NcD̎Ee2N.(zgznh7(NMlQW!1&+g1z?oZ_¯弖5 Bѭh1m% }F]fY(SX'xu 5ǎ֐@+s"S;qGkoi(k_fv 6HSeTx㍺DlG 28jD1+G$ե ?u8s0dAό*Cxq." KU28 9g _E/bhNG`݇'yJ:6J: h7~! ᔫkuO\L'#4+NJYfKwгҝɂpV"ۆfϾ>dޚj[L!K!_Ƈ'*j>#dJLDCpԨI{<;lF%c) \IQ#Ӱ/Kb6DTVo"HqO7n~ב:߅^G^bJ, 1(*F{_Bjڤrq7V1:_C۠p.v|[dV*~`rG?)&W~CIvRRy}+ͭ%HP;x, ^7>wr$au_ѧ?i`2<=&:RwTx:@OO(9 LM*.ćp4ס&fg&ɂ$MT1tϲD@+$ fzb+x|C:&D߲<~Gk^P.^C [Z+1~隭yk[[] (jA"xs=@0>9 y"1V+-mĕ;^[ˋsc14-2F+)8I(oM5i5]jy[bx@H2]b-s;^I9*(W'ቈf<{5#b4h"#z0 s390 Q9:!A.aƒ#>&" V?)Vnk0mGv5( r,&3PYk+5ʹb!:ۧ{<,on5J x&dje1`&f B"LQ L*s֡&;ZOCjIT"'ip4;ά*oѣm6q7z)7ؔMlsQky‚Nkbܯ}6Cx*˨"}l2 )G;k¢%(aP|ܩ#oNR,L9:->B|JA8-&2P*&@Ua?I..lœ ='5hgp? "gCኌ@U-xZ>.2fDMqE3-Fd>ѐ.xNI Y>ϤWкF bOXK9ވFp,1M[BGz cAB58+dհl{pxOSf@Ɠ8@O<57/.ۄY?߆%UxOjzށU$ < r#@-0|h{w7;{pkLyXG d0oi_2KJgQH<ѫϿzg.ltl fƾ=w pO]5jAkt#n>>Sx  |a^7rَ<65jeZgBF":^"\ڥ|?5ý%Ml K_I}- ŜT z:=lCHSF3 X)Cz)i55^aa4-7cHˏ<̘_ ;bB 0)cF DZ'huH|!nf0…nlfӭ|~ۥ]t?h@\~gCWYҞz%.ƃϣn<>jsj3Е;o;uDeHa^-:޵Uư|V cÓ_T6O(, ?gBZ"HE []$|ȡ};uMee5Әu>_B.ԉ%0c nM"`"ƨ~M3 _VRԈ],iđX71o-A;"3>O@94ҷoFyTOiV,x¹/9*StWq ҍQ qrG.Xi3 }儓#?Wُ29tKÅe"ƻ Z#ӈm~X~n{Fm|cW%ukjm&ۚSdddۮɶ6Ιl]vצ|.=g="<>l4R( EC/ޗFlo{zwAw7dcI]:w.gs2t B,Xe([#8:FW )uE׳X2"%_|é%B+Ŷ>b'Uc<{kYm~j/S%B1X3NwI+Tq?zSq;pDN8 eiΌ:Q.Qو?&ky>j^5gL}M>=8lrbnRO'G=yJm̀G@BV\p>+b i]PIAqJ2X&3-MW!aStX瑄%!%'axj\.P}VxוDJ: On> oiGK M'3.C~>}*?f׾O?LE;_lǹg}albu lzڗ4cV,2z3*;ű5j Ǽ-h,{}kGG+r\MJ E|?C%`h,*NvKѷh/y899#OH LD{(^<ƖF+Cr=BƟD}7\v#Tg.f70~qW'o;( U%d1rQƎ5`7 O3;hǣ)9WI#%kx. \K_ *` 8ùW8D;5p'/d\u&f*Ap/ R2jww5CF[Ak265W=D?0ɩNoA%YhzV#S7C<,.t^:5nMVC]( vÒ FvЫ7B̽L)5G6ZNo'Iq[`WG})zHvӣe;27ɰ6WI{RS\-NUmX~ShI0QAc\P h|j0);r=HLz;ؤqڋ<-(.j5a4x:"iZVF>_T2$/%] x~rY/3^x!% [G,>9 Da-[8-ʨpYɩ'onRZzi\ΐz$^Yi"*ц`$2N7{<{:G$."Y|J,9_ŔsETdO8glqh!Gz!O.<2O}mqJ;K}'d>KӘ7m<ʑ%(mA9[Fc8KyN buw<\^G ܄:Jp-v W7ڣ7~.!mxOKN3;:eGa us嗀|tGcd,_N݇QcPoD66-e`Uq֖umuJ͛5rj!huPc)oSŚP؞C25Xy^t$~Og t6)[c.k.En"+~cF9F1CC _ոN}4*@k0]'Pɟk{3/ #1X͏xjƽqwm=m|hY-92wK']R` OiCG|?\6uQs4ꯚGqj0'29%*C 60;WkcFLI`5ԑ p3g +eAo& 2`c+xC>U$:hA2BN+EҌ@GČmᇊz_kgk㷚0a QwIYi{O>Y2V{!;tVfwi$Բǽ}fiP`"oG}녥ʪ *hKLh֓3RL[B-2Ev@ZO٫kЖ"Tk?޺k <~< ٞ{ظp8n6U/m%[KH|I&u`w0U`]oV|ڵI?"ʤ0ΡY=U[bu3%1\5Æ Z_cT_I-p}_^ŕmݐ6&R3(ujyN4EG: Λ"Q[yRt_g/jVE ^%;(&bR:QgIӋ4QIfrVEuUzdÊ9,WNÔ_ps/$AY(dGF$m+h ]5N%I?e[Qu"I1bɊ }?㉛Q8Ĥl쯨$. .Q!o#cdzx ,Tx ZdDv K k;{urO"Dv d5t6'f)0+hX :O+Jepg=/Ntf]>Z~aߠ Ϻ!n8UB/xgmS{ujV*<2BĚy^ilGSASI.G֐ROZV=l/BeQJo%kM$U`@589? b~rEZbʌz;cz$նT_Y^j;Q%,9Et'x|16(]>g,\EWGıF5%7.ɨAo4t{YX E=oo~NAmͼ,!EyD[ҌoDdARyRJgr[x|)m_]^^~|.PvP!{ Ֆw!t߭?lo͇&О` Bg!-"%'5~V]zs pNt];d3DuЗTOaC [RsnشxCv??N P1{T W,`"ڷ<'/,ٖ* yFr|ȡƧ!G#Oe+~;Ld` aU{K^~YGAqC@K h] &[J]}@Z52\%l7u?Yj`il qsǢ4 xP'+0 _SL`bsOy#pV4Q[E ճjoB@_\JzA_-Le)}V43}rNJnJ;]e0;ދSVW1KKbVEYE"0gulޙig_l@RnO\ s5k kj2y FUV1\vYU.,eeAPM_=7.Pz56pSXU^!#tpѴU@u=D!]tw<@ Q]Hlê.G[om %9"\.ŏ][B-GgrlaYB-ďI7!)Niězxbf?} O-!ڡM &d>q;ўwY%]Y8tU⋭ ?{\"wr fKf]x=N䨯9q=p#_8n1SΟ0*wwނGonQ{~S-lrFCUFiCSq7 )LXŹ8$kܦק(gvg}f fŵ=QBÄ 4⺅@mqzʲ~x)[C"@9Bw "*;;cgDQEI|M%<8DI6"^gMO)`;Ar> ]SBiHO@!]pՕ݃fz Fb9t%##)n̎E~kͯlZ*'u+3)ý*=1B%Meu0Ks50KY bY%>["] foOwvY#xW;?y<^LG/ASԅ*u~.\ 3ӘM-+$f '[>2e_"+x>i`k8"d1xZT OXI$/XH%/et\GbK?0Kzm!jѫ:EF =]4ǥC~54nib3GTy(MG=w_&+M|Ϝ0gT-FL܊foLt,ʅFT@ªaHf-[?ScTc_sEN&ߓY*LuNb;_`L[SZSeƏLO1_ƈeMկt,;vLٰ)p1){c |>Z/gLLOՊ(=g5+1JmpZ3.˜bǶdl7 X/4rS]s#(dXhhPd[ /MYf ܞtb)u҄㋐%><:Iб$%}Ͼ޺ߟi197n73rq2z+shj 1u@Η%@oc'`Ⱦ5UL1*0n貯.7+UL=3=p&}ÓaQԢ- aw[MJ*n{x"H.L{_&GH|k-o)(gRpQV$ ,[uz?Mj^ٻe\%P 9us#.b_kXܺy%mFѤ[ *Ꮕ;OϐխF͕3]t tz>4ưzL&YmX;eW O]=H򊅲٬uaw@y*Cu}|ҝqpb)$OL絅bCb,IWwfF9*V2qe#l ̟uy)@%CKKRTBc}781J+,{)H;@RgF36(y'q DƑwS)-SX*`ejn2QDJ"<(QyAQ238 ȏ5[.껽d7n)]bNx8ȈE,7g vaE ,Ҭ~d Tmt ӄ9>?dt}Vd+eپ>Mcgyܾ/.b|ޣM3s0[dJ?B"[Ї=J؛X룤8=#/qza68vcs0Y{/LNiKL"tSe³d |7B:Mݗy72sEΔR]sI|V\>6UX5cjrrw|+v.*]8Zmt)LRRhMxi]M9b{.ŊŦ 1ۺI3#矋޿ֺ浑 :D8QF*RmR3bz/H"$!_Z쟬X XpBfocԔ4$ƴRa[ŲO> stream xڴsx>v&il۶۶Ѩ5Fc64 w{#G29׵pZu3CI(jh rtpcdeb((9ڛ80-L\lL,,.@7kG 7  ly"P@Т9t3vhL*n&e"bmi;;#LŘr&fs"@d8:LV&vG P.VSTQe%Vwwrrt. Q% IP ߒZ];\QRCTCWEwVwFbC jhO3'%?4].Ы 0 9ݬJ{W f@W )-ڃn&wN\(ceO ``rt3qswcͩEwwq]C\]9:ӷ73qpwKl_ k;ohA80y; ` =+o$A:9x3m,XX;[݉Y(+? % :^fV̿ 3/ͬ !}&v@k psq`nmuOvY G &?C@ϥJ N@ f%G7HsW-)w;;%{ ;[yhQrt75kW)k/տ]4v@жc}IفftX>\K3[+_a@ofu1 ]u5wlt0s4vqrL\\LX@ e 9a0398BNn G `m0Af?,%YR+Yb0A(ҿ(`V@Y Ainoj4Pk ٿ'kmԄ_/jd巰S7˟pGwB, BV聄v:YAjAAAt@V2ZuSW+ NAN&hGoD bƝ<?#YYA%+{?r8sS9=Jv&V%Sԇ O~< p y5'h (_rL>@3E}˟C8n.@mks_.&n.^z,ӞdQύh11G/_F(3&PiXӀNſo hhfQ Y4[MtR##5I'QM.n ʦ*vT3H v(ա ö{lX=sm*c@"):ŤTMN{(Ic>=9q$6+\k!g"k *AlVBI݂ 9]Xl{LIuCXh.D"0@ Kiu>gր"n*d xRk{'Yq#&G^+?]tɓt[ L{i/ADOT:I@bv\ã"T6ÍK{{ m5c$=LE/gr|iAcb_- !mx!s]VK}cEˋ.EI/(Hn5Vmq_9,}$ r#.go]6fQiP MV}AnEmh/A7.~1ᢉKF*8[cktduJ8Ib'ɱVھ#JT&Zk1 벯918]?(a̕ߔ,)pvtWE W_i"Dd7JT ΅VE-ZYKvSC6~ }aox28ہ(IK]Vmva1:kZM/Ex\JV!@VϞ'68*/6a#|q"s'xD-Hht8մmaÃ6B\0cO!SU5ІF"f nUL~6'Rn>uV+ $a"h]v:`m4;0f!U֗>V%~\ߢ 8S( rtycVK]RyshnG{]]-E+'yr2N b&;2&Zva^|[b)&SFq')~UWLl[BnROs;N_=eShsY__y':2wrR ұM]3]=ShP Nŗ|]2(_ĝH_ّfѫ-g.Lp;uRd)#Hqnಝ&o]Jho3 twhH=9:QC7}&vF*0o>9~;=MT` ċcqY(dYjJXIT#c 4./bڭu{!j?!Ur9 w5?_+NS܆Hߧ~1|NGʈf:Ɠ>k|4:koBu@2n)p9NF'!~xYxrm.cVO:]ttfaG'nv,UjX -#$GҖP[# v݇2u|;xBI=P|Z; Ɖ'ZTN(.;sנo]&,efNGsUE8rC%A%ek7&߲ N\2t~SjqIƵ' {H가@.ɮv|i.LNGx_Kb B&pPԣHx&瞗ْaBd8F+ C[X-c(&ꙇ8>9MEm0Q?w\v󕁰D^MaڦAtu j3:)c4(ّnLJr,m[ %wAWoHZ wnn<-ϑ$ՍvIܾxс AP=tdljHCT5y1'S+&ٳl]z>oM-f'f.i"7bhQ" n_^tjhJ=ГLiWȷ!Oqa"`Vnڨ &ΝD>=$.f3/!LIeˆd"e1Q qL¼==ȯ"3P|,>!5'd"$}@PO25CnAbªNǚ~H G.xN e$uN4˰@nzu'CH='䕣HpلfsAb[7A 5,M=?_GzN~jbpujڎ~-;Nð+ڊYiպs&߈3%!힧a&VTߣ%H\'@"l`Nmx=K3ۢםg'£vZ+cZSD1><VH T82pX>ڻLI ~Y8E C[Hr w1\ |/8d@RP"f;RLZH&WO1KxE, `C<_vo(XK3.q{o:j ow\hyT6_σ΢]æA@59RIg\!ȳGnkoT(wC2Ajl&)!YPkxX7{8;%D.&š6W͕אpSvG;:*G&˛W?=)6sMɧ#mC5;Q[pYםN5"˨kɷxzŷp A 0{z='ů)p^lIOjzhR/UHNjxO7w*?4yOôdF/׭9cv[eF%€i!;l?Iܔbx#[8tc'"ԩA@iXw}`^S.b:t4־jD')|4j?-QHEBޜº4%I#G7r@jˏ₯M8:wf0A X,Gd=ѯޱ4wR]ZmULFh `Mpc˦qW¿DCwn`;R0qA;|uE8XY} InI@3L%mRV])xadCGB.Ӈ"ݹ# nB Akk=^'/ $2[KU 7hTp84QP2'IrT & oJLgL!w.~Th}s,L:C"9q.'"l(h4: $ 8[5¤~1QhFN|/o"8"oqn-kh^Z,1|P:3r/V&MDKV" mΑlIF#eDFRƊP4mW ғZ^$jepk##7pii{ĝg@bN.WV ~vsѯ's8W%vnV!rm31S6c&B}!t3tR.F\?i!Ek#Y}2o21;O=ib&d}k uswVd?WbC K'z+ 739N-`s1;Qz#r-;J)aCN2FryhQ|_YL~cEoV hjHb׶Qgy!e42&\R(׹\Vłhy\O>(2lҡ|S~2@rt jFjى/y*VKơUhC%n0[_aSϠg-G .8Dm?Pt5#[> J`5,Y67~h (bY(6V=@~Y+;Cj!%ѷw]4®3UlɰJAwi)ceJJMֵdN K"閇I?g񣎆"Oe< bHV9xrT"S>cf1)8Jq@=6%OފVeAUgS\HAn חSǽ d,۳Kwi[uݩ [A՗9AԏN֫$QE7wd ۪+vDOn*t.u}7/-ŧ:/o-Aʰc55FkGxҳb }5U%8N:.$tPtRt)lmB]3P9RQFtXvVkb/+~z!3>(yt?-gnEĆk}4O"eNxMHtaVco}(:VmBYbQng 5{LN8M(foELc٨2i~ ${ߙno4Sn c,kB>^pA/{̧n"Tm *H!p63ZYv$=N(?  BӫsآZrNj/~L7Dľzeo$A]t(hخ߄!E0⡧AFlV z`z)~;&J r8^96s鴛"yj‘ 13=Z{BTw/Ҙt(He 8-Zs=4;/2)Sz#*Yb9/ w{DqJ 3PwHR5lXK53ҠjABQCQ^zm% 2:Z󒇅&PfۃSlb֒_$LrSϋ2%% 3޳odaIqYF-|Fw=HgO/`Bt8;OFІD`d*62*΢[PmZ<.7z_΍B >c0iW2S.Q?  \4lNw9K/u0"I2H #w7؆ZMR_֩#(Lf`N;-n&mgl\Ѐ%Alօ=:͘bg>H*:c`Dz2bԤR Ɲz!tۄZwÝTnEfl|?FXnf:I"ضJW+?K1ᕟǻhg crh{u8¤n48 bqLjDJ6V14`,CYgwA ±='w^]N*K7gp9KƒVךZ<0얉j\!pþppGv[;2{Iw-6I uvxcLj_ 9&t'UʵQDl$?RU&}ֱ}]@ԦlX ٭ 6`ٚ8Xz)5 ajIaߥ<~Xj/H"nS{gQdcSAzFDXٸmyX}I\i$!dcY*PN~;O:U3)r 򅯵L):ٟi+Ÿ!jT['NnӫRq'5/#=ۮJpm"C.h2bcsw,_!ZaAbwGW[y(YbO/mo1W ǪLUѹ*^+@A3-0v6h!a]uD|m7PTCWmZE("Yn[F=M^lUyi6DlNe ux`+%o]%`auzfӨl5v"ƟgwP+5_þ6&@bag@R dVyVbiOGW2PpmƎ*LҤAym[ɫ*BMܛgh`+nTG+Ǟ0M މ@KL^25-(1@zZeTZwd#FCs Z֬cMժaQz3*:fD/?afQ75P-|Nk8G@OV,6+|LnqJ993'IakS.)qmK^Oz <$%f"#/zq?˰hy%o>\cX. CMLE'{q:~"ǮMMδVP8.։sKcDAsɱ-a=hyډX;5܍ȑg}eJ]wZ;:Ƴ<74_3-<IFU|'Z`ԤW^\|f7fe9:Č-JvmR8wQGu] }L. GInU d+m7 Sn٘HbT:Hn EBٵc;FӘZ` ^+ig<,vzS&J]/.ж!,n/XImlMG9H1N{ܣɓOZMYR4+Ŗ;;Ta5t^=zt(hu=tr<[xqu,dӶ~p}N@jSgAsJ# w(oz2\dt/01ќ,rN/H7Y(/r~^&]15}:^z&y m4٪uZE7qף)?0:&%FYCtIr2@lpX=1FNpNA.m'j&F=#0sg,Kg 7flkaP}AZxU"D`>el&iXYcdJ?JTԛVnYVTr9Xz.Y WcWf^hw{]&86y^RCopM/TuMր9E̝$,{F8jڭ*p:KohZp]?J~O:"˞xGD#Z#o3ȣW ,tNHok|ȖRTbUS?q~|>λ>P/W~ԭWNYI|0hlI\p؄ g0hr4B8<ծV`sPQ`b">R]~[ьNڎԭD̠$Mow ~?wXTZ,$'Yc %. x6;W/Iʤt˖vzR:c^]G:[tyPTp)&pKk0tEU4ާ$8J佦4z55b&GNdBr~^r~K b_M_[**VHTnׂov1cqSONLfR3٢hBp"[OaG]Yc;}Qy׌T9J_Q:e.v;bv>ƈ$T 齛l_8} D.UPy%||4s;<7>oZ EGUJcQGQTe Y֗_PXa4,xc)ö1H9iG˿óZ ;\=s{Afniڌu. sR]H~e>dڊ/ak- ]A!)"CA+nnȌϊ\ bOL#Tn'\t3>=^ 2tY9A;§^e Bw!@a LJ~&;Z4J2KhXB:JR4S#giW`G{h$ƕg*AȕP Z仠irC#',ƧJ|S TTgs{F?!b|yH](y)6ZM'*`<C&Se sgoAI˄ۥ@0E;(f5`l}ۂoKj謽_j {oӣ#Q1u!hP\}عA2R&&8I2,:d Z -$tד:0D*=T5cF/U^&Qr8%f4]u@W~V!8 NukO5m3whQ?mwW E M(a1+P貔G+Zb_fQG˧@<ԴbErt^up {J"R#=W.3` 곭kOzY#?bh6sO$Դa mVF ͕ҳquѰ呾łwx -9:L=_7e1K&Ns& %/.a?>"D{hy xQϫgiHd.".[{J^%Z6LB@hOzCH6n UAe Q!͘U8R.o/Pb@Xݻ%AFCpz1'x .c Ft\\ȧ5Z]xr@r(+z1TGjry6 )!ֺT MA:WDÈۿgot_9פ[!6 ;ZV X԰X?vMh}/U ý5^ 1WF~ҟI8A5>dj1C lVfa︧HRe ;SK> g&*Ѣʉ5&šIfkU~+ލ!z*̡Kz%&C8=/[X>lצD)=Q7@Y%PO51银[s/EۅGe =tG ^ClBX؊KO(oHN%T2D&Ye5#"n<Z~diIk݂B>cEt7DaӾOS>c@?<50ofB G|44>"4yA&~DW:㩚Ru*Oә(107.3Pv6G;kRl Â'ARMA$ń=Bifk95WciӘ46z6T-'K?QOYg 9"90P 閔6)X"WCfxZgۘ'dD_'RС2$q OԕTp_s7K>_A#aON#^Z8(vsIci;LcYU @5nAotc".Y{t\al9ǚ,/ȝdoMHӤcg今lSEȽb0\|eKt G`gФ4?;;ɚȝkIuo#ђ?,ŝ 7;Hdž*)-+)V4[Ka#9VO~ߧ!mԻ1^$ڈT\Aa|vvssX]MiCs6>4Z}5BKA5@tvG畾ϸ&҇]vމT4#O/0lƇ_Hl3,x>V5PDr9Ѕ~ಒmO`<ygNy>D @:L8Dž{iEG!c͝!vY-:hi!°=CZL\ZɸRPNA˯P= 8ے=cыF;Vw7ۮ@tk%sb_DUàcըΒ-ں$Nin ɔqqy5 %Pֿ8CﶾK ۦR+L}|G +npg O@':Pl{.b3Џ vL:s׳da >adOK).Cp :0t|M&_ED4.9k!Jʒ? # R9&֚>l۲U"J `{`Ls1ĸ}[RzCTq_~jqY+9gf;!A>r}^çsI,oaSˀkhs aڿY?^ނp9Џ擂KSrM8 X"7[ ∀x,a=r Q*֧d] c6FyR35_{ii?=Ӥ q)}]'E@B~^1f„wDt$_Gď˦+@'viiiMFN<$4b2xܹDltĿ*vza}t̞L츈 V&(uh673IA#q_ic#gbϓEZCHtN!:'W,ZN'#r#=lպs Px gxw))*rz]m0ۆ_@C|h?WkĹCjlq3ȡ20(t#j2#!H9{[ TM݁BgOK;,%C=<5 g e:]XN>Vч@p+܃SGD\kO#y a|eP҇1Eߊ==w^WX}2؋yg1J -{?ڣH DbCY",lz">gV2G9jPqin'PpH_:#4q U['}X`eb'(;ˬB ģߡ ve'm"Y)$hhHۮpw gHm6WBܴ:p:oyn_xH;pbHfQahz`|FAXL 5 |?BG:/ HK|OdY۟jjpCF6w)ڳe0-vІ AmىhxWTo@> -8/|5>'~a=` z0K 6)M: S&67 ;Ɣi/(iHK l˅5DvSM{ paI{`AqyY,t|ʩTZΰH#yg.5rjUCPWK ATq_kdŐ=& Ԡ]yY-ta!ۖKۉ{FG9QF:wR3DT0`%4i 82ܵ" P<ƴ;R hknoxJG>hl y~TfK{$t)r!j.6V`X @vL𱹎=7ry p&ӗܤ=9\0}wS%bF̔#Z8ul{XrUv BC:Բ| *[IB ;39qWV?/|y^tdH`]>=\ 5{6{7aIDKEEfq "Xu6nާ]d%+YN,|~?LFWU.lڦWF:3acR S]ڹc/*e%4ۈʔv aMМxzΈZTjY)e6Gv0s}NfDbIik>CU)eśj^^36ẏoѠh}"r! *9RmcϵeQٍ`;̵+J}XS;Aaʺ GiHDDIv!힀QI$"N06YrU $(B նSU[T ^K<#+~f e2Yv{Ur=X[y$Wt^c_<ĩЬvr['dl2Hq!>*KheD)Dbۏok8uO= 13{W[iaM(qx]s \_\ ϱ0.K兿ݸmV/ u ;} LKjnQP˜ Х 3;V~&<^dP䮶֍~\55Z#kQYnym5?<9QL 6ʋ3lT?HY!Fu(5 <P|tʘ5SQ-L3V~O齹Gx9nv]4Vv_@Gƹ!liK&k]Y p[1SMLIF|h\zH$xpDkD-H'g]UʜOcȅGҩԐ%M,w_&O5n}[SmF+u29a 39TQR=i&`t(aYt,sf9KXi0d8IyV[%*V;h}bm-gαA^y,fsӷ*q\af$}rkQ g|7ՋHq_J1;fP<45q !R'= bm`o]#.65_ՊTb-P 7Ey+ڤaF%(=RIUG璠ӒN,T鎥azX>ZEl~C?X= cu_qu/(ҹk"qLJ)9TNq{<L Y΄+0ZKt> s5TkE^͈Rm%atiӨ>FWv‚ak :wH 0Vq's䍰"jwL< T:{ JI꛴4@h̸=%|oX!sds(c%͒OdRbHF2=/.о6;8E+z+/M%a)mL4ɉ{nuxK!ƯLw}"&wi[9Czr>+[>0H&Bm,S=:53C5}:MjvSS-?`%\Lkק<4m7Q],ђ>vnQR FW1%ܫ\ LӽIj]>$5}t˜X b/H哏J;bJoOxOĖ_''UW)zi U[jJ_U0u( ~hxŘ7X_jwBT򊐥ޥ1W1zbJnmc'L}W2 НMpVFym/0!WZz#Qf4!w*Kek`,%nZEO |NڌA:&QxXŠ$ mYX\;kYir2@''$R+GԵhhbVpAR[W،a?]$!:<k:hT(/RLzI+UM8Yιƽ/gI3a8]]z}X`اT"mq"$fƖ4 yurށt w >2T9uaM9=ĦN5iur(YfHm?8pߍPIJȰF4B!~؝=-B j>Ǻ*%lb36@/Ė5x2B4)=Mm'˒+u>%9 :F2{yWhGl/,m4YնځGQM̎gi~q:+t u+[1~9EiIUF25kRKǧ&lޣ6Yr?tN4kcDJ1'Sxf׳2s4Z&OHb,lڟ&zn);ǂ[)*K锎_!/JC9HY쾗u{^n\L+ֺ'^S#d9Uq(Xӓb0Ln+1 +pׅ:7#ܦ-v;x4L]8A׏Ջ)IcFVi6zNdr5ûQ(AG%kqn?tS ZЮ1t#<ӹISks^bO?,*6[HhN0/\.*о;i 2NrF#pgl%FVis }C{.t|N6:FkSDy8'ٵ |^/[ l=m9 Nj&p`%蟼^n.;G@Ia3)=V=Cg9W/oxU }aC&@߶4? E&!(2ZӋH49IZ;5ƌL|ǸjDɺSbiX:& [t5NwTCa!bHv MI`-y 1T@LkJyvsup=n:LOc_[)DŽ:ĖT2WC}|w E/GBq'²ͣF+VuTRעiD9w!Yeѝ9=W|æ\'c!gƺPp])ֈvb-YoiD~ZMwg(BZH3#[ܞ=A*{!+Gpf/"XfQٜwJ.<] }^"z)8n#93gPl=t] "zw5)|yj& J? y^F^9{yޓb!A{p%H0W׷2CE6M'T8~L -P><SŸhIUdFmWd i漜X SB^q2V\uطIsZ>EjְE rZ[DE}fr(R#V3OO*[L.3Let;2wAZrȰ'PLh{݁1hA<8A]9k0]A^'1pNQJ%9WPIԌWY3, mb*Kf!;;jɕ| |59 @И iB%߽ڰ{JNSUXԱ#<3 %϶"8 bšy0?4s?>w9 4HO^I앇pjSz@fCVG'^T<~ 8psVw_x]gSF#E4?(nPک׵1lBd?Cشup%j_ PKW_ 'K{ yC&?ks3Nǭ"P0d+>ZHZdJ4pRk*0Y;(EH2u'>n) f`}YQcc~K߃ԩ*,U׶R=!ŭ=}:>y_|s;hy;f)7A'c&.`W4!=ept F6ݺbB\|~2E'hlAE{'KRoh2j-*u}Ecߨ¦nEl1ipmGނ}VL'3e p\Kj!&lm4lHɤ([&ӹ+!QJPa*_h:֡$jmXQArC jV?n[6V~f>u,9a3%Yb#SVWafnL:|0pC s\^s*v E~"iYm++lfZr8#(]~gjWY u#=,x79gRH2<;nv^:xh?יZU'u edz|BD"p^ ܝw4p,& D1AlpBͥTGUv ka6O[X" ƀ{qK-q:L&*ʹzxnG9,&616f"> #*.|MylN+h3+K?Q5Zr.GT|s_O1N~Kab% 'soj{$]^S&8 5B<:+[H,LԉSVV }> ZGx^Sz&{2 e$ݐx(XcQoioTeUrn¤aA T9׻Ճ_L-ESծ,:ETiNr;aT@ջ6Y飲lt ̣g8!c]GnhHTtXB=9 ":4|\KD~a-X8Ÿ9 O6_ۈ-쑐#zB2{dJnMkѠ<΅vr]z;FavjK[SXU>vU $'^.7Q- J1gnY!J}=E<0!d b=ɿ}e>'"ڴEjN4 ԏ g_)j8ߝzmDB8&큅ҰvycNc++D/u=:!"pW&#uVY;FWMxS-O]3rAQwi c8P9+lEԹ첻/ÁmM}+̟8LyB5i>sӫBFoDB식q\mzp+GYꕂÿ9g/< 5 )^e<$@ ,@"!&-X䎻Sf6_"\ĐIT~:E0#:QH @N]q`T?r As;7`0,9]\(& NbߋUDc;W yUc͞}`ONvd βiDqB|&^ݍϪ% kbrL/ۇGB^//Ag1%mxt.yGꪵ[EG}yRጣj]d*/ZٵԜ n2)k{3UpwdrkEc Du8ֹVqkK p£ߌ*DZm5@O{n2YB5n^:nJM7[-Bѷ z^@BhFsk|FUA^d֌o=Yv<yjX"M 5dH3ŭ0?pP6<|P~YWnrepʠFЎrkg˿|̬wh26B?Bg8.|ݵ~؟)? lr+u|$g f ']1Z 4lotTEմ 7M.ō|cRfPWϝ̖\ua2-PxWrO?ίUa.=SOʣݚ~Ũr#ĮfY~IeX;aR>0;\Oojʥ.bPC83ә\lZd^6I>mFoۑz6ނzm_ڊ4<ѹj+#89FCx {m XV 4|ijt0Q2%ZN:ٞNAq M9kFvBHH#:Z t62iŪBS$zd$!FN8\ڶyqSVy*nUb*>HdA9Y~jr ᮿE$2mضѬRuD,XƪSA G|{2 @ j_LBsk3w5 VỚMX^dA|C/KwRacMW9\LE:i[]bNOb$|Ec" _NKyn6Oc+#]TvU /K> >ƌB,DphiMTZ%cuSOimKҌj!L ?"t٩V⣺)R".I Dk +67aEV9<;q +2y޺ŦkPGv7E5+=D4B6npoBIbv;ݲWX J*= tmB& ܶ"Z7o(n^l~x >p2 ~W^;7Gc+ ;Me-waP׿ tyVuf@\v^Ru{g&vfֳٮ+6= us mܿY3lZ_\^c~.+\rѩk8 8ud_:jlZ9%>P`5düe=]ԢTQ]ᙉ2u2SD5DepkByHԂׄޛ@^Q\ǁpn֨X+g1"H nzkIRq)pSJƜ5oFJZF݌@ Z4jۭ: !vy_ (F~FG8n p5Dz5tG|z,ŕt!tFwS\$5.s, NI(f%Qg.0q~lT]͖wֲ;I5`|rإ1dӰW9k˲0Rr??Ec6f7J 6!ۭBz=t+l-/{BI4~ݞkNl"XDwGZd`F=3X^h+*X8לɐ Z1rFTF5a䕡e)*: ~$} t7~(E vG7KJ |lϞCX`+ҫ^m"yK!`;r?wx"fQ[FՎMO~~RtxcPX8.zRo$Ap9f'ٌ֔B׷>R?U"7Լ`'p\I~iI =CflvW•en1;W Kth4m(dy8:rSU71!v@~<7[l@ @n}TȚcTݩ, K 3A/4ԻfΝu3MI-$ VP:F9ڪʺÀM40!SMVF.2gM+@, ;< "#PkOGH]6\Z+"so4r3K\\Wh6Rr7C)W޹k~_/ډNƭP5 uT_g)@1,9/)=!u4$Ĩi9!k5aO&:) ࿗؍!B{lv`%{?ٮo_#_nam)>,grDTDJv n V>p<G::j]|I#*^C\=daA hu.(Ӗ;?ǃK{MJƮ_N<[t(1̱/M " f)On_'LtMʯTn$؇ wJ+βB G#@͊pGU@أM.d!Ăd.EidO; kA}p+UTygQMTQV{sbO1E Tﶙ 2fpMXnivg§O_?j{ DkI/%3>9m sUJD'뱫VU"u!]ߴmu4ǷMDMy#Esilw)Ƞ.89 is \.zL+7ژס\.WqHtT`v WBsr ]ӏ ܻ6T7Xⲁ%^?dRo6~fcg7xgqpp)sV <<9D8=93sFLTzxz͚^{Qm+A{Xq;ij˹1 m2l&o/<u2Ί1DBPdGXw`Fk a>H/v15Mg3@T^,)HkPlɫ>hbx?: S_m,Ě9wB!ol}ڝFR B.HEmIWv\Hv2/7szy]8i&o5L<y8*<.AEq w(jԡ ~s2`t$}I'3&h dHw"^c~TK_=os$ ObϾ{lN9\o1^Ftnw;K~<ʐt ",t (&67H1@bryriiB1vK `wDP'K{(Sl|fYku+."tY`\Eik՞'%z5O1@H['@OX"Civlg *sEcAKCwnJ!_vKaҵg&(Qւր4)e:ol7%c<$2c>㏺IҭF TRlI|儽+}SoSpd&=h endstream endobj 97 0 obj << /Length1 2003 /Length2 22002 /Length3 0 /Length 23270 /Filter /FlateDecode >> stream xڴeT\۶5ww(K] $!@pw>Ͻgs5,оsZ5&%(vtefga()k95nf66.DjjIg+(e T-\r"@G`Piy9tf5+˛h rҿHA6jp23'[`fap- ,,Ǜ;́6fV@ ֔jjiҳtsr;IM-mY&4W ߚ -O4;{܁.?m3R5ٸ: zxxXYerxoWg=9ZV:$ɀtx[қ.@1s+WIMM `rt:9ZL-iEtsvC]n%owfhg+fm[]@..XػY3_6eqyiM-f792+ߦWzRJ>6;?MҎ`7.'z+ً pX-͉U (/?o&Ŀm@Wa/11 2woD3w O"Dv^%MoV`oL?"kҿSKhȪv}?;zɸ۫9L;d ÖN`f_> hrhew5{ӿ=mY2iRo};@/3;/diatqpq M_"_qҎ`K5`lnn-v}K8Έ / `U7c8Fl<#N7?Vvr6\o7ߐ?ߪ.`}# }|ߐ3o]=Hxߐw7@B9l<š`;. e3WgdFр`Of7|o|;9x#__M h4 h .g9+nd'TP$=0$'`XGcޑZ3qm.mO*->âXMA__593)@{Pw+e:aBY6lg{LEt..H f}K 3E# N=X{m0Esp^-c-Ii`@orz-pޣDVv'}bHNuT>\ahrsu} ;nJMĸ+A{.7IQdMՐ,qMZx"B-Z*Բ+t5'E7CKez*@RT=[g)Y;C^h))v?gry[5q e<rph,rn[gҺ˶&LU:*+1VP zZ (& Ш`ie`S;}D58.lЯ u\ፐ..JNVcg݈>[/4?Khɟƀ JDG SZìKfNu',(^D!)*RG.Z1Z u3M`R,| E L$h=J J=zߦ UU ?`n>B ӍEU׌ q9>lQ *'_SݸôJr|eF,RAwrޙ Ds+\1[gd<$3a6?-9znE5w[Dy<*wB'Ax~k@9k3v-bd/Jɭ ):%W}b16cѶV# 6Ex:"(hPҰTYr<\lV'M(hky7:i0uÒ?GJR8T0 JV$"C l{{(ǥpH#U}DL0Fxdc01^eհq^Coؾ|ƢlSOy!V܇0QjC.FzsmRP':ϬCm\%>^}"4cI~V]⣍E%QQJ:So xPjz*c}yq,Ml,C=6~OJx +SW3xI{Y%3|exEQXƈ]0(HR$Ƭ|sv)wH?%|Q (fdY. =MチeHaZ8N1s&gp 0אk|LH.Z`  O_0Z~b^UūҲߑʝwP}HmKa"AQszN9S|Gx vo#E@A3OQ@>Տɀh}mP* rp@Tk]aoB>JBY321CԎۏqnC+g^ՉvOL0{ _hjVkkNCM['= PtW$!>Lb&چ?TI-\lʭŤt Հ=,+ϭ[Ǹ=|gN>i@W^ɒgJ1 NxxpbQQAj%6T$~K7'iO̒ {\. u9g|I.gKы<nON)K+vցXz4$rl(vu ( gOS:~! jI08JLљBzUV 4aC)O( f.hjI t%j֥ٗEe342בGzE_k=YQQw$&vxV(cMNStA㩥m6l8W/Q @)J/}F2wsay?M i^@tפf8,I<E)2%9˿w#.𺻮۪xO}̑-=K7O&g6Q<+W*ݩHh;'}^u\%%/rBJ, UF#R}mN?,Tv%uM82}?U̗ǵDւL ^0&j?v=U{v3 :cY'S3tQ(3C\(,EK8M9N8fqidf@Ío0 ?\gDH]at3N" P*+6V`E-j{CL;ܳh}ri]a[3d/=;p}cʼn}IMFBe ! c- 3laL[EţGGN/l&ɇj3Ir ub~Vh/ $p#c^b7S&$10D~%~Z;vZ(ݍY_3=s42Rm6). B!i^613:YDZV925k]`d#$&I+Z &cBWʴY(iݤ?;LMw헵9 Qn[٘>ÏŜl^DbnrW^i X×Y .( вƌC|Zܱ}[9 ԕΔ=?LP_+j1grn㰑=у R"{nCyɚw,|jJ$=ޢ'B?oUo׎]C<aؒ´V3!d_g@e%;-fbx1'xA=*4؃הk )vM<-x1mqk0Bz#vM*i 1nKf6ukW]6?'VsuN}*fvڢj\1;b79"3*!EP^JejIF;s dG]W?c/K xg{܌զ٫/רI^ZpT3euN ˥Vu;NzW+،9N"PT hB0sƚ{:G&s(6=Xci.6FE CR\P'.r&a-!Ɋ&͂qj%7wIrw 4](<ZD8lc=._]4 K=p7ӈh)}X.#:ӷY DY-~cV[Fк Qgl=vI/|l- LW[A:_E$~\%) Dv7ݡ+(8Mn潦&ȋ9%ERL"*A#6ttbF>U85`5I͠)]ª=aHbfAutF+ͯ"BY%4b-*|o?R#DM&>k%‰'b5< I)Ťځv5r]3QQg].O K]{&}n҃=,3a3UǏ9qk)9l,t5B܃hr]Cs:ߘq+} _PY&I껅>L\ $.V u [O[#w nZ*sE宄QP!Q޵wlb[RH>o ek,@_,sj$bM}x\CvwvZ]2-|0HKM6&\iŁûփ"z|ԉ1aO=1v;Ѣ8յJy0kO>1 r&9; %c`tu}"~j5b-3njXHRWIP y戸~ySg&j-k, uax+U? ~XrնJs4MnV0@pHHt _~~}jtŁ,I4%N1i!w&3!}jvKl}bX~3UOc+w. {5[tV_MndK"쓳a,Q' ^d+גlć%=zCΝZ}ҋd2Uծv8犝:Q%rU].ܕ'g>, e5cK+l]3}w]ip\K͍1%>.TMB(稟 .tIU`<] Mg36+8~-w,y0cOz)Ւt%CרVd=ux@b#|+B1kEKqG_g}%.%%Z.e>-t.8R._8D:i}!?~P]aͅP "Оl P_TftԎ#Ft嫏Yb&G1ϱ ϡ7) :^ PCrbR䦱Y569)A)nql+~elms;@ZYM(Q\-GY*ѽ('\v@?=?Wc+ }V9,B'iN1oSw1$ۄbT/3"⛍f?K ]Q5$Pdz~62?hDþSodY':H: .R#M<+ubA}s|^{ hD>͠@BttS2i&+M 3mc[VfN=ii>bjNWT]{ |80^AEzPݘ6EtʉXmTKOPG09<] (bsK|Zq87ji!"i 'lP] EcbWl)Y-ypxn?azpK/)0Il{-a>WL=rH}9`{= lcEd.c{B DENys>D%+ {/Z*Fd9k>0E qh3bѻνMnӇ-k}֢l6k4鯄~0҃pD{1Hѻ$㗌U? h L^/=(Qx;*qQ5V;uPnvPiu4<p~aI V<ǖNF75r^D`P#_zxP}߭&tP@: |g^2u/ݑ_azW}"b>Ak#ĩ:U1˸REseGmNr$ܳP{Z]oЧThbKuŃ@ۀ)ʩ=z* (K__ʂ*`CPK$a)sN߆^as"5n?}{Vy>G4Z duWN3d dV9V)*%$nN"B)#MbG-ױJG.IL9͑hhڛ w2h&4w+(mh,2 ω9 #3C$ʘYOU0Ny I U790?uga݅djі:8 $B\ZQXRT̋93ccT*쿍 $H*Äj!y66闹K>n u?| vxd$iJ.kGm&|iLTbTo ɕ_7Ѹ6øWtXJ("2]?PQbuz2nӄ#NSUomXr{X*5K|@WitԱxlWE]<-BW((3#-yj܁l'qyTw\7)x '.m >HS*ƾnKoC3զ/1;)V(FNRtƾ#[;9茨:Zi4c`SJب)$}n#Zyl}z=6n·Z*'H\~sq'ZޚU r;ѧ[ֻaY..PqB&h _O/1(X3gv3xsXAo*m  칅2uN3 NJ>Ԏ&b.|@|6c)q&@ Ѝ]ڄ8@9]Y J0MO7aUJNwήL6,1#FQ9L)PbZcIgw^v,%u*3iUC龄[G8:/c*R۴*1UT4jOV r*Z>¼JQMzX<EKڂ,tNЧ擋W8!9qSNFS0Q/,\G#N bΎf-sbgY^ q+h75`/su~kB$*u%oZN0tʹO4͜ o>g/CPIO'Hc/8q MvrP:9zz^э3Y-tl+3C% 'QܚpI;Y D1_'ST쯒|R2[Pwv,hiA]ԥS&x;Ar*|_X#nT z7N4x-#.!yם0~3m^j>]uh 䯐'zotp rδ#_5'OEk4Vrܦ3db$~Te* i!44 xUWL&T) ?Ne5[W wA}JmF{VnX{9@KEtb1jm2n*b"7[q"dTh:V=Ewr|К3:/e=AQ)8d}%uڶg/!'c/kC U8/?saTiUx@ɩyA;_OpyZ@ 9j80zVHFB~[Q; pH$p3ZGx%3g04WKvpЍ|7r8̽V(z4aʤnԳj yb #ۈmڬQj+ђ. {|R_h_<|MiB/P0.}$ ;3,Ze4'&AaSR{zJdv40>]ؿakI^ĽL0:FlԬ~?܄<ʓBpzoJ9}̊2ݦ=Ҡ5-304ޕ34WX>y=\w G)P/C25M?# LH8euHÉ !`)02癖Sށ~koKo|𞙆mQ='a?n;7CLU6˅?o+pQa :<'w?z.H}~L+d3!ZzZg),V 4{XC RUZJҧڑ]1e_1x@(A-B%(ӼE#'K!;e ˔ylrc^H= 9PO :8WQ2rP:8+BRʁҲM}Jᶆ<)%!9$ Tg {Ȑt<J p51ƶrp[gGF * 7Ա3UOm,똇UD_3ĥι8m*| wvP)`rùZq$IM;MC [ =mJeQ":&={i1ePC9CHd9d5kh\`rBReyKUr==WtӗZ_.˕pROIQj{~R'y7ZZמ~VS5Nꤨ/x䞤NTp]Fb_HBz>~m$5ixGx{K &Mx4k#v.W4Y`ώI G%BzM%쁽|I\-I֚vW^cV^@ W{>2Tcf|0{&$:$r2KAp'S͎Έ2:ndvidXôg+}R_*i^XU͈S[2uuܐ ]'G rt_6s8_:VG)aA?t>aڒ2M@z֡`K@SN7sY䓫I5 }_# ^#)EflDIe)׵eҷ51R3ǖ+.ZQ3OIH*0{X|D:D$" B&~ '?@fyRF,ox# 419(P馃" uvD,I6f'^9uzhYUeI 䅒3ʑ;ZW@0gC2nT: hq05A$5w!lD {?lW9UHU^~:Qчy-r" l7qqXw_(#\;yzx{vv A`v?R|귗#\E;DYp +8EJ Rq)_FU_jOm]!h~^sGD%( |ˌ373Zpr!`\WP_.E˷P{Kz*M¥zWHTYE@ȕkRZcMlިa lhL y53"\؈NEuJ0yO*>N9rkK.i)㢊)@w7@Z.VF฻xy0] ~* ,+<ZHp[C z?S2ï mJ{ŇAWUK?g2k՚)5~^2_ 5' yWY;J/%f6*[UQTaIz-;E%y0B=J^c#wi5OÇCooB6.8\wO'޷!f[_tN?>-GT C#M;Ef[$Swr"|p)ugg\>{2>dj{$8ݴ_b#L "Qz1癓>]~$lѺMx/ۛe,fU-C>f+1#C~{| 5k$̸(rQ.ol,.5,o!(āgAjq)w##?Cr%XS~e| ɵqA>ժ'Ÿ"SvzޭjR)ʗgޕ%3h=V>}K)Kv% H8Xzݥd~$w0nC6^ଢ଼ERľ,($fHP&?xRZFHl_|P꞊2Kkfcmse*w04; [+EeQonÞS״N$0,Aq#48<QwS=:rO_Wl/̬ ZTi1i8ek ~lnZJ^dʯ dv,ְLfg;L]UOMjת1Zhݖ7s\QFpa}k=HG^|(HI#lr,. 8;OĢr0$^dLDp.3ҡ{CvK͇ȪI/֖"J',q{+ΞI$;}4!ó"J&݂òS2ji I qz gۺ FQfհ_>zۗWhQ|c=$ [:/ V=_j VbYPO~<>{LJjpFH 9Dž /E.|2 5UP=}!=Ou+[>"ȉYPi1d=.#s1os`U׀plqͱldCuOxuE_8?aݾ$z_4y1eRASQ]d'O$F 3 7tGRQhoBrCL[5t=#+x">W:!:=Qr|jɸ`;gx ^\4 RfWEdDԎxE-# ;בPs"^B7[*nAɛFU۱9^CWlubRA,w6}}:0l.UUlMm`*tUȶ!ًg{¯jH"X43<) Y|lTJ@p+E ˁ![f:4\j!>"i(:bv+'8S-jX$hZrM&yM!q:Lu_, ,Ԣz9r|@TU#CriQ~8b!9Am[&&b@wՇ+S<;ꣃPTWٕa7_t'q|^ZaBRWbZjڜSsLg?0%B=h)#8aӹ1 pjBnbVܪkWPHv>`"s:1#g|ln8*R;i])jyY [=e|y4\z^0wTɗ$0>oܚfqZjkJ~.Nӯ 2?JV<[ cWQ)ix.Mr9ר ?'(Jfe$l181*0K>.kX|=#Ȕ0ZY5;OpϩЄR/4buY3ϽRuHl(JS ~L. ^rYrF#\0cWNaQX_eD+"fߌO ?a[(A2E˭Ku`s503jyRpyma; =[$qXGպ*nwkEpP'aW,Y,?7\A ɝe~)V)$եtRȵ4W =rzMW v!~t_xHe QǷ!VlA⍞3W>~j!=J)Ýkg@SםVBy~ @Y {dgVKT+sy/fYn?d١* EL0%'f+c?0YuZ =;] @/^VLS뙁Wx\(wPkLw|ӰU")Mlۆ0S -8贒ؐn%ܪ Rmv*GHp8ր;<Y0c-t3WU% 2yjWRRx֗1_r:~GhY.FAie)˜Tp0o% 5yM<݌# V'DeۭnxF8WFAPӐ=# w+EY;M| SL* M:7jj (qUW&p&A穬 Vtf$:Y 1@r]DR[aGs Ӱ-ѩea?Լtݸ=SZ F9zn>i}팴+c/4G x$,ǏQ.4Qz.SYN&(&{GWv{ͤ\F1 krܢʇf[T[iӈ"6h E m,h7(, L$S`!w* <ݬ8(>F>/kT9V8UU۸T X;iOԳO^EStrSb2TY`ꬺVx6<ݿL驢=v6yoeIq,,}CF oPOݒdB xge2x4Nѹ+LW!>xv4.D8ޥF·Z7;׾=zgy*%Ԯ'vH0-hOO 98*)9"ʗϓgAؼ˫-[N|tamb.V+(sr7]KC2ϸ_po' QY h5gy7LD370uDsl4O:k9 ؊4 eJ~ x}YI.?[ d[ҧu"ynk Z5h"M :lCTȨ,f{/]l81G.8z"Ś]U S$}zLۖ;'#gtIW#DZHp17%& f3Ę2R+<]~[T$5-GMNC)k)"R>|ڞ:I5f! wY|FLɃSr! ٱ< Oy/',ȎݧtW|b0Tݚ>O' |[ޮ5"`'o0Pj1Vz 7i{ͯ)Sr.8,%L|Y=V n@8R$,n[0Ӡڌr+md(+ Aq)ӛTf2wg 3EV4da>RtَO+#Gng^`lpiM)rny^Y*fL~os Ȭjad#&Nj'e=)F_rV>$F 4h@Ǧ$8͈=Kܖ>O n>vĔmvߜH'lV}ڂ=\CL|5a!C00$V<gs]fhY@D&V$8HbYߖRSGDܩV0rf6OY&nQ2XDs׮9)-B纇r{ H'b:y/_ ǣ }> ZGx^SzçRw5JTiebڧ1>pYNçr h{ӁܕXGƕkN슘\DKT]GB ?w@봫m\Rg8`6ZD']EJٳ] &6'֣Zn]ʢD@ t\b4`[B< ãuԱw?>n29hCI%8-DUl]F!Ĥ^ī]9ggl:lw n{>Ƌ, PK17%rA]4F LFhV+/Y(bsSӦjTLRd{5tɌmbƦ>sN}8Ls᣽bo%CY.5+ }i (=ܫ]@MN.b kP,}& ElmV7.jBe=F|Gw\6oü-K$IOɢg%TS*kLĒ`}{e'#`? x2N!'n;a_(W*̈L7> "?B`NbU5l$4:8vwdB)\[%2p{مHذǹ5e=Ul| DgCpq }&g 9i%ŌP'0ر]#}>9hoҚI Zg l|Q C[xEv-7+qi|l0upcFkxCw P2~'Fc>}ő\cn ÿ=T DŎ1݃ԑ,oZ5ظ|s%Lì]=UxRp?Ĵ@j꫸Aܘ-6U M{{Nx/(Gz%sTϔi/4ʁnh?g?*V:o?DhphcrRdZ9h[3QocZgj|5OfeKʵYw|#{p`;?:J !ɸOޜ0cvJrqrU5>a$4El&mqCS)O<ŘZF򢀖8f~r+ 2:靲KzWF|8L ތVP@~ijr0at  億[>b,)(VXriӎ.;QxN1%g/BAg5f2u &o֐ N 5.}u&LwNٯw6 9~kLC5؀44H)EpY?^ Gaq w98._r}aCx%ۛxrͦc{pw kO7Ll~0g壈ʝVzPMdP`լ*!?uOia Eg//Ɖ_z4:_5ziPn(M./w4GZ ωEς֡A{3sTph;g=ЁL%_H]@u4d0mZ)@oxܣ@s TPG$͘C-俵oe oZy|GniE_N䎦ދyzxx"׎h~qvrf`x萹ѭhpTZ`?_HA\2@1M.N%4*.u&{AGܶk|/" { dOhT2OI$"+)1Z+>iT_(aD*O.{g 0DpK77u;䟽blVg!xGg*t{! CclY0]C> stream xڴeTʶ5;NC!и[pww'gs7zj&%S03raHI+X[h -vFZzzf8RRA;C`w#Z}( ziC!w M )?\m\LM`7ZP2HJd? k+ `mP2T(+ +(Dd)i?+:XO-Jʢ!%a 5@TYQϫG<ܥp27j# >\-J0qpᤣsvv5vw3>%S{98Z|v eoheoIoG+?>[G#Ĵ`ohiLJI,VV@+C=@/Ѐ vvrHKeiU7돕iY{{ǀVn.[#S ?3Sd2"ŠJ4Rij_ IqY , [Z[Z~Tm}B}rs6vrPZ齁 -36t m.&tŗ?b?FxXFop@'C?T[Y8T/QSk+ W%(%ha!4?=oCj)d,31u143u720ؖDF⃻ @_Z[Xv3hU?DU$/mҷ6020vv@W8.0>m`YtV.GOܟ ecIؙt8>tE,:}kK@ 3dЙ~8Xr, te s(ߐ@ώ9UۿW?Lv.\g✵>WYrcPRHRړr ^5XŴv\,-bC|ft|k)1NmD_B5tym.h _Oi'1Dvztzkl+@yH1qUViJ.so&4; d܅PG`ݼ Q (.dmq߲vx$akk~hI˦OUrk "ƪ)Hd書Dab&>5&nIL{+P80fBKX&nM%ZsHRպmG/ܽIZ% mV蟧(߹0T\x&_>c)ʩW3f:=OP5;[V^EM>a٥+qy-3/ fɹur|r3%TȞ2? QuMg8rz?;@; H]<]и#D#gL0gTt=P X'&_TE< ޽=q+dsj'.LnO MSDG$&s{X/7}1<;$ji۰v]F9)hf.I2E4Hu`t+~CɴȺ"ҧlQԢs'O\;hyq$#ܬE:x+/hQ=~_vT"n6zk֊SԆ`7IhT+LݿP"n1HCDe9@4Y4KoUPNk{^־x+I/&Br#sSelP|u=SRZ*yæk5I'n䊓 rsR\&%\ 1y[Qk*[ͯ-TQgz#UrK,|g0U87> ɟtJ}P_]Irm5{<7뺵v֠F)%Ivj?o.N1~(-9wozW chN޴{އ0@jӔwϽrlJ T}}U_ 5=z ζ"MnٶYc2[ V'UZ|g~9A;0+npT6Pm}%P1"NC[W2yyJZe&AGWF= wlK4QN~/YPj9@oGġ,BQC}XuSIQvW65v˴$$gS7EБ7X|O->hdil>3 5+n`=45rA]cyNmf|Sk9鵛4ұ^llѮ"m&[/1 v 5X*MPkmSfO{./.0U1 ]o +QeQGv+Ma-$s{+-g3S;HutUϞ#kBr^`i(LII6!~l[͉%C71bTl$裐gƄƈڊ'%3Yvna߸W&Gv,ZEM}N7I gܢ,/,I8XӉ&tV?+.{`N f]c |{;9<*B|l$*Boz|7M.WkWK N{NzJf(I4/6J߳J I6EݩݮâAI4M=7&0 MC?:_ȏ: aBa=-:ߓ0JnG8&fIS_ڌ+W\Xsf,%ƨtiF;shZ3w܆r#o$frW H ҝ̺?I'(Yãr/ AWNZ;bA >ڣe5Zլ5x3D4sGк1f 4N=S3lrA!4L PCw6lm"5/#+z 蔊Ş W]qrZ,@Q䅁>iOFf~ЎW3AXNVtQ _l;G 4billn.׉y^fTֈ0q ՙ*o {. }$X,)~r)6mmd,>rcb3*gј nx-[PꢤY!S ^hlhSC2QDpm_`T:[jd|[;?}f\*AFO @r9u󹜙` g^$,KկKq +9gmH ގlL_ XT:o'' ۅ㮴BC()*RՈ`,\@RP:zTx=AC(1.)GER⃔xh滯2YC1o Nma/^dlqpMkBEA *3z<9C,~`] 2K&^s p JݏaY{hr<^3fK6]#JD#r/bm/-q?y+w ^I jPaqJ|шoBEzҶ-fM'y'I@b.J![К8.b4b9RɾeԴbd!g8|-hn-pɬ5XNjyCzjAg.&@n]瞱}99(jClS;mJe W)c{6_Y~| z]ULUl~Q"c~,`!Z&[{?!"6.}|a!1&)n*h(hKu:8c~>+$=)vy*w+Mzy]^Q=Ȑ?Ym"H8T 9(n >QYy6bOU 4oIJ+Q7jwzx8-E6TGBݟr%F_̊*!"Emiv@z/3#glm0!DWqebvU󺎒c}q61wJetA {~OmLûH4cX{vrkMy1d݋yh~M;X3'1YrfeYqGg rRzKwQ8фI-4iBI4 \*ff(Y}X}\ףr"a3f`RF%E}TT6T5dnVID:\yN JÝk2ұ< 8Y.fj_XVHm>숙{z1w3uJ14aQHY<+?I# +-Quϴ־bg˘vs Cf10R[SDIS6S=K̛kR8G+J+bh Z2Ié fNcS?!Hn !Wul'݋K_7|" FWmc<1tѻOzt'R\9!1k~>6?qH?V wT:1ڽ`oigr~4_R\ l\H} "~,heِ 4zL^NT@H}WсHM =P ɕ;)hJH}wt̐wоג gױC)U^l{H&םd38DN*657}߹x#{uLܬS.V"A45)!٪/M.$?k;ZZ_ƞ)-͘5 /k:tr=(s+TC#/ԡQ ; `J!;dZyԊ{[&)xA[Mc~9Yy* ^z﫹v\pw5% g~ϻi(:9x i)zًAh>Dr"*z,TXIE #ydCNՆ.5`0yʡ@3tݝ]qxCql~Iש!r[3=;N'ua9}UJ"cĂ J c*$X:8HcPZ[ \I8&:kA6͟HB7s 2-Oav;vY'ɖS/Szϊyt,@)?j+b<(ʗ±BWQCb#ǧsq6z(N+oaxu"K0Z/*G1yC Q/q8G}tZFu+dg/uSYK ě76xCZFH%Fa>~X\ns<2j(n|<16 ,S{]~V,3U}{F zEx-#rg3ڼgMRO=b~´֛.9UmFD~Un2z7Gj0bEc`}1pz#ojׇḾ]B7y\T#jsa@ ~,S?gG%xܗ!uýd<bp_aӉyl6U38(w2Jgs_ |1D\4S0b1_åsN7"l|!kج_*IKߊr rš0lQ-Q3Iv28*1iMh5j|nE;5ieR[k1(רF ʲ/g{qDFZJXtVu.=1HΫkV_:Rh;"јYPP ƄgPk UC PИDӯMh=PϸeY;lͯ.i{B ^,'hKu#ĂqؖNntzg!R/DdL|q21{M X%@W(`#nfREWIc:+ zy` FY_ Xr`3=a-{Ug'12tG]aU3*]`#5H#}a j0@>m(D R(#vKdIJPEI w<&3gO$GL&YxpkP& )`քceE3n/+yqHZm@`w/BcCV`Zkx<ꦗ`YFxkt0Բf^֙btO`* M%w<3<! #KIV]+:nђ.9˕w񜎼wS5Lĵl'&ft[ '.x]wn6v1a~- +4O5 h OOAοӿfֶyy\P'<8h#Y"oGp4鮈Vdآ|nm@bYݓw\rXFI~Y-׿m[NsчHKVFj1|Y(bggܺH{4oXK" RFLe4tGz)c /g)B?ӧ:}wW)mۿg?Vz*8@R9CXL^eeGE_b CcPhVH1f i]-^5x;pZT+-UZGߵȂ׹e/<@RGY'^e4mu=%]7HpzzZ7a"nNEE#jwx$um)/J/WȌ)m{QQ|?OzW!YȆfcF9^ˉߩF'DhWL<G%=ގSJk|@`/7[kB׈ 7McҒ0V7p}sk3Z'Uo .oI$Ve,jXuWQT]%2WR}>%W.얤k'}<1l0RPeptG=`8q_G"B.W)p-yK߱v1  tkj*ѵrpK/sYx@>(u%k{F Z/^L΅n)bI<7j;B;{wq\$lu{8}V"&&bCF T,_Lё}倵RͺU<IHsA'y J%u3˨[zQl_v ֖Ks ݑUůZt{,S~,:OrKҀM&GѳÙl!8\M]nOvU[y.OoLj:P&nRU3=۲ ;;\/|f`f$zKZ|Z%~+usX"hG XVQEKh `S1K>#%30D nX!LԲԫ@ŸM懦(xW0@uOѼ3;]U-zAX<8j! g$ŘrUq{x_ Jp0ޜXA*UIhb.1J&4NfA9O6o%^'{xV~6 m!r# (HnIl.*~ٴ> iT\) +0V:,3%CPN[fsLϠ'VeY$ϩI$RZށ.uI7ӄ",QҧtJ{Z %p9(ǔ%%-HDl7B[B:Bjl[q`8r[q͠4, [l'> _U]&J *4Top\ӾWngc[RsZ07$|Kz/NY:Ń̇ :k\N,iL)^[ 7BO vT Lr 즔gP1y.n]W98UxJoM?6Dj l 7p.kω]MϠʟDfJtrM'W"uK·nIOr5Pnm,0 oL*~-8ύ0m[ =v},Ŵ0V]=CSarL ^25o\m U<-M !#he?@BlF(w9Q-Z'7kˈU{{C5DT|i#m^3ɨ$$&sS|/cN;3 1z~Rw8R_WY{>pʚ귢e4L bu%I?'+W,pJIX-Jg-ZM< { uq2PV#4 Wuv&QqT!.K]Re˪ƫgfx`PW"@:-9Gڷr$t9Mh* E /^_EI!/${*R! +*1&L9~$i2>ieU>jnz͌ {g9=ON5)ߋ_^Tnw$}]&y?TW][t鼑Ǵ0hI{Qc[ږ^ު2aL Ci-"Ջ3\ך:kW}ӍXS9"{/wA7[ Nx XGr&XnCD`U(|HC'mu.e(Ϛx,A7pSJx;c>0FiΘ0jQXAZ#8yhP5ogNi3_@_D_9`yM_lE!}c6N_=R:$~Czo1Xi6Fˁ9TS 80kc!t' K ^pߝ7 tS-LLEaˉ9H/X k:5Ğ MMacs,T Jʆ(;M@䬔HU_s &CD%kro=2-|΋P.g+hFy>>x՜ s”F~&]RFj-{Qil2!n5#kU6 N%J%'$R7.˦ SzAp(LM 5ReC@[˰1T6Ppd.=PBYrfQEQ:yג˯4~A8bםmVOtrK 7H1s<-&}mކ{.N3DE%VQ@XŃs^gח"Z"1BotXKԨ0q,XlgUT;(ϓP :oWʱ؛mRvWyfTBYR@lS(Ƴ3AUe9FFwՀio7|ɵj8RUQaVX| ;j;T aZNDV5:PLN,t;<5{Y|#:rN՗V ]J|=Dz V=Up$_/',g?JBOZF<;RڲoA$vm$Rw4!iI*+Ӳ_oĘBd2yaZ"NT ,IU IRlP[>'3'oUx+ET8y<)BRkpK}o;4M{l?lwE|%bе/SK-|4Sh;1L˔\Q= 뭃<HQ02sd 2YX}>4b-a01&2Nѱ]"%أ|  YK*`8Y K0D YF!_^]=uf*a21{);l@,iD_@FtXt{`1"-^T?闒&NPcw)unQlf_vuC)ko;Ë4scH J~((\q mϓRo+~0N{'gc`RT@ ::u!~䬮І_"DȎ{fyp؄aؤR*s ilxd6)g=:$ sў[Kx^tl D+o(Te\%kT Ip>IŢ˜[k>S:!!DhA:N̳kTቸykx_*ۑ2gdC>E"B_ƽv2FyN6[W\Сc-4IO:wxe|Ⱥ괖)$*,Oh-ē6-VU>oDŠgacYdЛ83g4 u{aU7&c.b|Sy)kbq6߼zkdi҅Zrl|{M$8k*xeKcEHk)uu=195p=zLm|Urr%$RK2HNXƎh1`Bk'a' pA*-gl& mzš2[ xs_΅1s!3KR+U|zI\2i$?bXVF6x[tp?StƲZcQ@`X0rrThb8֡{|MQ|!Wºkg5d8i[;ˡQv!v(TE1qTnGOer(uTo:SwhM;6yn)2< Wf9z(B#ul9Մ:j)Wky7F5'Ad-n*bN}q3J;h;/"]:Om%}yS$li ]6ǟ~~W 85Rw_+|)QA_NzQ8M" $rjК8;jzucp}SQՆ^m_`}l򛴮l?zaHJAJEv(2[l1 V|ɪT،%4ey,A]3CdMFm>_hqnsB0EX"m3=J_=u],f8'C"zKҳuI;&ją=u'ZWK1GYy_ve<Yz b)ssءENFޔUi xpt0fopB leI֤ŋO '4Նe}FoJ@>1"OQh1  Yͳ * rt-PSvPxLWί?d,8ְա ̧S@j\!oJ[ؚf $v.u6-֓l g}&8Vt4<~!?WΞ/914&5J"-Hyw6(ihX:0_[<<ƴ`C{~m[ÇXrKm-:gٶd۶l[Xߛo}]C(C=NerU2` Zm௟oNr'CGc7_2BڤwjG6Ԟ'! 6?Lˡ 쇅\̶5^5 &XfhqJQ+WL4l *}4óbãi\{cAn=fUI@?S|Lf2[Ku`?5GpeDsc?#Er8ޏl-Gc@aJ޴A"̛& O(/+=M/Pgv+}!@|+ƗfgĘ473Н#RTti^-Ț+3+}ƬndR͛O+rEz8ȅE9_].{Vv0~B&ƶ:fEw]@`"+fx@0|~{aPt`|)!nDkֵի4(%^R oӤuM 8+3x ` W?.e0k ' #F=߿k` sLs9ήWHܪ *1~Dk Ok9Tz!^^W.UkMaZiݕ$2pPFapfamx٧GSxx72zRqE%D'I݈9Af܈S{-֥Zy*ŞKӻؑ{%UAԎaNruW|Strt/z/}Cz<'\)`j.2%@_i0My߿ZKh"W/ŇC@ ] ^;γB%`o6M߬ס7n^ ި[/77֜du)kF}ld2W0%t9oqsACl|: ؑxUn!Y=T SnX1Bq" #3ju<@SZ&"W.\&wVGXF@n Z^KD9@ 5sk iN;pF_ӿl!7#vU/rҾDmZwv갆Ʈ!lT` vH >Zc'[Rs/*^MAbE|*^=\-RW |CTa'"L$9֨\VIn"H a8izFIJ~.N[O_ u;ܳb#Pew+ \bI5/{ӡS#]^ZudcE){}F-d "$󏠰}RZi|.IK1G?B:8ˬ I/IoBgRH"lxj\]媏N/(,Lѫf8ر,lX"eՎ=[j|዗j 浵Mb$| {;YsӞn(4Q20-o(o yaUeԖ#NQZT2NYm>,fac-KbH'cMHĨ^Ӄ9S|E}b7%Z=7x>8E7`_mp^4i*@9ZM[ȫ'fDlΨ:N [&ۯa n+է]S4su/`Ų|r-͗ :=- >jA" ԰I2G]JGE J7SUzG=6ׂv8ռg̤lZK ĨݧS[75h ?a3c`mr۱,IYOϙD0Y.D7UeBlS3.ˎ(ـ 7 ̓$ܻR#xpq#^~aC>XZ׫ Qq>LxkFZ꺝~͙OY,^^1@rP/#GoQ3ͲlM;CxJR4RkGp= 6/-"3Xf$z& ĹTPV%aR%W;az,&x8b\k1tF7"9G3hFC7<3_MUAG`6мdF-Os k(ng0X0 'est"0v(axA;ɷīF` g{¯-q.N}߰c|: eeSyޭ VH 7ڵ7h"ׄTu yҖe7?Z%Q9?v^fLOt@W}^e@ONsK˩AF2qio-pϦAJo1٣_i3pRB}[|,[8uR}8p荟V٩qSMUɉ^-->W^SVw=M,@'dmH%\f&i _)g +^S m,.ʤuxǡg[zQ; 仐KO3<˓N"񡛈-k YtdD3?le̐K.#mOˎA~kys-%D$ʪf l*n;X@!ƿi#+0fFn2z<>o=k(qذ, =0{@ ˆ\.g-6U8x5U)hr!.wȵ&&Zb C[ArEQswu3==ulj 5bT2%D ͷm#~8ZL'556EPEY;$NmJpD$OBAoSwg4OL`OGTv 2 XuD<>(R5z҆R;(Ŷ d׿oF+[UB逡_F`$\p8`4x}N"Dbf¬e|t*BI 6,bfТ (,2ݤft#,-Rxjw~S''iE @'=g;Kb39娑ks}ײh@m)ځi-\(^$MyY c4-`iӸxO۸D6W?<:"2CuM&֠0 $N+HU<;6.Ej_²MJ*st誳[< dt|-}E<܆1[8@dJUJ|D5D,knVczRJh_tJl?NƶG `%!VV2yV[5)+;.mFơMmЫd؍BX ㍁^ʥt<9U$kϮpBw2[ܪf$ Z ITd!ߊ=STcA/? iqzprJ,;sOa5JxTu~_M*/r_Ă*jL)~`ڗd[tQ<@g۾u& 楌moQq]$BH[2RSB7F/M?|eӴ dv*, dʼLq,O qLE7us[l3%6l;%K8p_ǯ㸨wdZqsA@VrŸPi_@! I 8[K$]C&$Ḕ|~G@`; A*JHK^&!o! uh>wCA_.zy-, (={lH6b- (>`݅VNTҶ?5Bɝ=7o"$ڃ/9S"%¢Hu䁱93 jhfl$LN2h` &)o0BRdF q-Rs1u ~*C~UAxSJ c#%$kFenʵ׸߀&Nn9 ;0 Z<0M{u# "Uw휥v5F,H,lGa{jj#EӪdڏw.eЌT ss%uNŽD΅6Ϳh$QۋY}zEqptEM'+p:}(1/T'OU"p!Atk>daD 8V>LD$ʮf~}ľ|øo}9=,S JnD]_ZócnE̾A,,'L;͟+#RX!2Z̼P\J+@GM 7\NW>aM=[VzS% "6qQn ]}61k9*cRM4RLir뷛v0\KiAZq*-p^QO:5ZO3 /m7~Hlÿ([m;Z``Q U͊C0\:n$p6f.&BܸbIAwhANe~BC[U#ٛ[}w55a8\Y]ȕLkK_s 7޸;gZv!v.X> stream xڴeX>4" ]ҝ=43) %ݍ ) RH޺y0W\׺} %(AX8XJJ`Eh`degFtAl )3PX@vv~,t:-^% L 7]!,fP7dm 2@S$N^.65XX~W- P0{@V%V2jЃAsl45*Z nNN`"% W֔ZjAPeMwht%iMqM=Uikp܁.7Z(3jT+? 6+b?Mt: d bWߛP\dr:B&A ]_W ؘp4A 34bqsc~-EtsqC.7u 0te>~fcf 7W￴e[AUlٻ3[?6%qeyi MEXPu@O?ѿK)BG Ri$|RP `/gA`kYZV͉M dh  vLo3o3T?'gk~7ߎF(|K[ tС +0_f(o9 Sj 9x,V(l`t s#%83G[o`G3ٺz-Um!6/< : k tO1i>Mб>zl?,|N= ?|f `SӖQeF0id yC瀓iK?`c!`vA<6ߦ!^`ؤ ~;MA6? &q hw?]vWU@A~A׮AkA~?4 z~ObV?oYo 7o㟪EYTrgXo_)P!@uo7hPwPml"6ۿ T+ t(BCr9j@k^ 7GO"8p@a +2?nh'3'7Y]Kf.(Vs'-vqCtrpkaPFn@D"p@,otKh&P®f6ÞP g=ycp*tkP=@NhQϿ _#8C+y] <%equl-/(A\l= ءwoFՀ5WӇ* T=>.R-5 }~g@ y`]JSXtd9 ?q%BRd1T%P]K`m!XQN?X&i5j⧥ض?1h6VPb`y%áBN^ tF[B@kH6sGJ<Yf,ELNNX-N\Yi~ިKnL}|MݣԖd(\BEbE餥 pa]lS'M?`)'Hj]VkT'5 З 09l E:1pd. ;2 ymD_ޝl>ܧӝfS-\LS-N@7a+B>X3B߭0|W/+RFnFD⺓dɑk[,p![xs(". %ZvJ5MF+M] ֓o,wK.ӣ*(0#{ 7+@s;=vyg'Jo͎!ҼA~]gBš*]bl%w D؆UxwxW薒7خqQٟ_Džo,rYp)R6/r X~&=Xq5]>CT^uLgBM=/ΜZ!Y5TU4%r}F;.q WQ =D6)@%@q>sЦZ2>{ >Q/lm0D.ۥrBV&KTk9WOH/5%qgW+v?4%L ij/c(TF8e]eġ2r22D]-?}L;T&B@5a%,̨Л\݂F|zrPa9vfO7v3[ĩ7Ń=J`N^a䳍)TP4SHV%l9=ny2 $dC#F2:>Crxc.YONOž~{W YN= G)&;g]˘.%IZJB#Qdͷ N->[95쉒{aRhd#NSrmu){JتigFJX|dèT>*MsR תPw SqGsbWOs2vB%2&O%# ri~ַJq/Kbx$>ll YŘEuf%`AM> y!BC}I9Z95)ydn-7#@>[J٬b6ycMB@v)SyO=Enn]+j/(*8`]l-@&'G~ E[uB*]Rc+`b_Wɋž~N7@RqҬZfR1y/QC3c0}2*;v.KlZ`& &k'",A$I>$CX(:6_HL+)ܡ׎Ӵ%{]VKw5mKBWMhbKk;({1WXb\}'ど69Q%bG~0Jt,ٶq]rw=4OF[fV=V^7 ˼v<16!*d4QIןAm`Zgs8:pXuֿdLf0{3<- ;Juca5s M.D$"7e 3HcuAEZ/ NҋBK8N +E^>׶fA}+|IJR\? z 4ЊqEW]?m9'}ȃ}JUt:q<_"i!y2-nӦ,='i>⩱mXFzXEVѠ\D'cB:Q5@xL7E!,t42&pZ!m؁8R#oR>Y!?iZidm` F| [FB ꆞ }[ͩJi{b|veU!\ 汽vL yQ㏨6~L&1FcGe4*^﵆WnQO+"I1Df+nϧ1 aRǁa7UH1ᣃFęN!zF^.e@YN[ZӢNKFňo_۞T%~F/ϯ邠,Lܔ\eH˓͙Bm ޺,{ bo̰4;WW0bqse|c,{ҳÁMBib5ѾA'Qڢ6}`"@VZww='w)ٴo7/5maMT_m yT\&rD* EL\'u593"/tO-Fu0|Biͤ\2Xy6DAxi~:blJ1\Ĉ"xn/,G.eRl߷Q9l<B:εf 1oSZgqzꋉߣKW2.̙ӭ%afR-A3AB_=oTe !lX}A; ˦"(qG":dϒ6ۣƽcAR>4η8KuH92m>"FoP߭2pfX4mg=:OEf#Gܮ8צ+9Y3X4M*XSPyk9,{)U=2|NXUg=?*?_i0$b|S9PT\|)D:K+@t _zo%D||4lgLSLG*FWfo%~VdP Ȫ2̃~W$o$k*2]kF --%"ۧ6FEXz O,q%;|8s'8<2MkEe=n"#'s0u4cޓe6Kt`xl"h,1 C't>c}7M`c!;}v4瓈 ?J;_b%sSNʛFN!gi}= oc;>0Rpxe0i>}3M !ۺre> Ƴ2,7șHuȫ (_11V/aWRL2Epm8WG7p k#!V&{54Wy:Q3j T% |_ÆY*; x؞+R,DuWhy7cK_=msJeKKyESIXC s:ǃ`Kku_gĽv +p| xg>Z%ω4@=LD^ÃȰO>:0C;# JVT"2yLbsAHn6ym~7yiQ[엄9( q:g W.0Hq"Sكq <Ҍ_KsחlKEꨦ08K=GN g!*tu&)_m-z2G 7 K[Jҕ4aޫνׯJ9POhK;`}rdfE^E[ #DБ=9嚮MY SU0hNJ6y.E [\Oa^HN{MڌRGGoC*3ˇd$H(AIcË`&.` km#j0n#I|Uf͗E[*-y~Uq]S}D5\+6~TcTS)k Pm5#屳y}@|b.3vRS28۟-BIh"ׁ\r'jbA~Kw ȼτqa:֯$:{Q'z<_E&>P(cN<w)[Mb|`i BBkɏ_]F kTn*SR8Ղ6hp2 F B/"R,䓰!#|v?k{,{GCڹpP//|}*#<9,Т͛ lb?ױp1L;a81 V[>D2D\P~axIDK~S;&&e5ĩ]ȎIWd{XnWإdsqzZH(/»F65/{603JyQi3=3٪""pzs;T@|ElvɋDNz~yyڵE~W}5qi"LoD- /Af%‘~|"`]_=1]J_z.O5.D&ɶV,ghqSLKNMAUD1M&N}0 Gm0srg dv*Sv{̛ |y"Q@',ߜ(>#Xu ɞ1փwW*s:6^ QMEY ]Oۢuj,OHUxT?3/`EUF# 5hHP ^j8z%^̈P\8miVcfZb˿_xfR`A6_Zgx,-Y/ͯBIܸOFC6T_R3Is/Ih/Es:i)0~Y=n昗q :-"x.уfm_K.PH*S]o#`$0uyS^6gt~(0q+a+u ~>qY7r*&r뻂a?eհ _*6+* )KW -T1d{B&J+;=M:,W/.]/iEO./}H+"ƢCO(6(.p9O>I}(,X P◠cl@Si]d^"z~9҂܅2U<)ab+Gy+g2@dWw9kq7nD=?ݬ C~>^$1W*w*a]0)"y[¸FZdRP?[ ^@`pIqyՑfTJgw>.b:"X|U҂l Z2/k% WEHY!.xύ3 L.^OFVbhV QZ9 ?C6Dp0rm}~N]ebw!&@#.T¥BJ{/:tB>J(Ddрo\~Sy0\‰1t3$І4 ^`J8 H`:nIz1I}#XcyW1O/(k@p(w,>Xw\I!LjJ'O%3bdo^Љuycä+݌<7F5n̨"x\ OMt :VE/iUTp:5$ vY5ehP|SP[W%DfTwܘ@|tO#r^=hTc6"FZb?&v?nevrQ9SNa ZJ4Dx9;N2-mlLҪ'9X3֤['yѓS+jcd61K`('+oy2v%pgc;]k hR^»c W ?WJ>W<S0><7dlz'/=xZJpښ4}Mg\ETWij5_QJ3F1 )[bkbRSRܟ6[VM |\7r5M5vF;7pV.cDB }4Rt2'׹GijѸSIͅGWVʈu?u'"ia{s(6_]BvATHx؜m`*s@>e#*3V ٚ^(| Aha9^+QfӉR;84*e[ݩ$ w7)ܯWRnWlK燙Yp ,?EuF /RȫT `HpPȲ9[@n1D!Q9TVu`& 7b'\Ѓy)y}#FŒ4k'}[}CԓWQ}KN Q/Wfkm!e9wR -Fw 5۾\b#r{ 5Cr4hV:w>*&Tv vZ+>sJdaI/ NE+C)6i޺*0RF~8|.l*}EB2ZfLۈꨵz{ "Gwl~Ld:f/D{h$#htD91>G[1t I@ROw)XQp\1<K&vq%ոPcpq1~ŜB>Q7F+>Hp-sz'ĄS1RP-5h*0S|LY!VcFԳ:OE۸#pSx:HW6 G{ߘ3^kjNBS%mq~o!}VUCݐ V8Ye8P0ObvYzTv # E4WZD91r5! {'B$=`#@W5Alؑ 7"%'.Lԣƪ/EĢa𢸦x֫ ﯖDRO4%6FhoiVNSukWxO^^} (9vGְ1 exa Dsðyv3`!)YgA7XQ׉;H3lemV>bt:L{%fIߐm'RU&W1xs Cd)+§T)HK6 d2K0q Y0yw}Ѽuεzro]ޚ:[$]/((rO1) 4i]A*c-N6?iv,N Tg gR ,og pB|bЋV1HazDtU֮gf6Z׵ٮ wdQve8;BA RmQмƭSٶ7pK$z"9;-402jB;&_1M Qx|r_/atEN`1{A/jDA'l3Q:"&2oXCs臸9?lzT֞<0bM# 8ޜ0dT8.a~GH %:UECؒ0 .>JH״PUFw+ buTxt 2dZ4K}?N߮WC_=w(UUsʷvTuw50{.QIkm.Wnl )eEl I0 ZQZXdIVt-xrT|Jү1/"xm{mZs:9aA%JXSHgO&IJdk#+Og)vXT#^Ht^GTNO@1vY%p׾_m ]3783fiZ ȖFdf [é{UNymQ1sv:"?׶[JUߖ@,a3'ciC^ ΍-!X/_.dź;U73o }9 )D99qxrg|a}%K+_>4DJpa[I!S۶W&kawm>iJӬ,Lj+|%7O3ƧoOZNv%{VYb^9.|( hkQqѡ6 MđV0 fӘX'Wj~9u947sI4f},#RK~os:)eKq;:Яs SqNF>""5;]03rM :#vU^K 4 %[{Β.:/lg hhϥDvY_>bٻ*A=iVҖfv{Шja%b?MW z3ςZ}L1a->t9  yN\,"&=J0Sq#}SlP ^?f+ʾp4h Kui䵬NZje{>tMVJJMw05xJO9&G+ ,)F{2UM:aqh'E;=!kU Ǥ#ȓ.쳝r&l녰A!˝f́ƹIK*^ b訉D孎`ҟsKe~[-[O=kjF)2ixc>٢< }{x nl'>o+/>,`-=}9wZ!-*uP鄬-`Yf~\B&C0 WK^t}8  ?X%sqϸJ`'s3-T9yrYi23:{Mi\k)Unk, E4 oџcbѧM:YrcE .^ߐOmdUUX ׅ*%?L7Kt5hh=U>}[Z7XQ1A$A'􄙴&&D+IUbW> N?7tr:xQ˩؇8j=۫Z#2@#j\ le2 )OĐ γψbد}ŒRN[f+|_vwUt0.l~*m)ʄO?XEn+  Ld9hܲ:BjEˌd>eY._ǨRjsܖ4Un;P7}42 cL(r-&];̛g ;葞Yo2޿(r[>앱m6a+Bᬿ1SS 'zQ~'8Y1%͉BXj""mxu׷2*4Ay"K:%ƛƷ{T}oL>s[/U* =F l*k0O ) ^W[mkㄝ5+Bmح %<"ul6ɻɍ=I9aw¥ $2r臟;8}ӣaW}ކ;1U) j";$pvNV\zv.}U|gG{C9!hR y _Iڗt .S/,|Zr`GTD_ydCI;]ᥨV|2O "2.* e`{G*ږZiXF-ߊĢG&vķ9FӾ ޛRYqFqQ62G;  HZˡE`^R Y6kg/9[ߋc_Y /%JL<͉z.9)8-\ /szgzC>AFK9w)ZyD2RaeSO-祒*-Ӈ؇ckaeLgo#t}*^~1?, s5`-1 4p @T]D:>SԾ[ɓNE|I՝^6=kJ:&/N- RXiʺi@ܝJel>FC煴vGjwX 4^*a`N(s/:򘡴$.pL d:Jy~Q;@6_t+{_SZ(\ ݊i5֮ G  ]9\yhXT5jw_J~Qy{O0-2&A6y,wIAҷG2m޷cbYɻԤCX_$Mۥ\1<|^D_o?[!΅ :d q[uف\U <J qB~IF}:x/mf8Kj6EL22r DL~qeRm U7-u)t O@X9IM%cFVzϗ|ӼRFg Īg|Z~f0pRbiqBI?U^w187:ӽ,D.7PuK  ŷFfF>?H9+d쩕9HnkXi`k[[~R]Q&rEI"2 >~^UǤz -X+8k1ͅ_=s o9 ~¶BBlvܠMyT' ?ZXC$(EnK]4[ޮw|LBi'˵ex2ص.Dq CuUdHߌsZYNN%LJgwJx̺,?'/HZrVDۓex5`$7lV5tʙ+=ڪ=_#o\!jlC]pFwѢئ>uQr&MB7=;7y,ޏ wFu9J(:'b9(½z(?9*oRs$;vic 5xA4llCQm;,N\[;8u%H)7_;G5ty=Ut'=6'pMEE9>*A!UN}+9K%]&8}խ"l6RrLA} qk$4Y5\Ju֏ۍ'o jO{d(&zU= CÊN2Ʒe3.)lnn0:7RYIH؀Y9$ חL_96CJ)v9(>V5~iA ِA^9GY#6.I  h0!MiI-s av׌.k.[* L[9u|GOsxBD76TBSpHuazND hpq QGR>PO'337YͤoH vU`f ,{U V[=($ ,;#3(g6ӳ>pI@<\736J]?=]tzmԿPOUr/)(<߼_ 4!@< 7h|EY#ѣzx? OF C'de=QҕIOm_590RIԘ9]SŒzf/aIp FlXYzX6ґ?lc# Hړ*Eh_D Ύ+_?6䥗ˀ=/\:>)^|~i FmϿwvL #H:P֠W.(<+Hy4uVqGBܵ83j?I+{!P&=5ku(; `ʚ}C4&I7 m|L&"S9"LP㢕d(,~cQm&J8s+ PaD$|JO1֒ zV`:*3p%1e;ﱭ<&-]S)jA P<G7͞{P<,%ˀots+h4!^#HJj$hk-3廠 L:zT )W4!kD;4yau s; y`t!8!˟nt78~BM>/ju՘Hc `EdmL@'lxN}à>8b}4 ^EPObjJ.zGVdcdK{@$KsAWD*թTةW Zʞ4[=GuHjX D=^[G\#& rOq8ILʥnx.|A⬈\\x|!WZ6^(VU3.װC\Xy`>mWRX*e=\?Z O1PZӌ$|" ̚@ }p^ cJ-)L\-m!y4OG0-4/^MВ2KhɷYF7IؠQX[H8Gs 6r- kZ5nܐ+^S߯#;;XtI#qW!yaص(!3(Q|^AʞxQ D!)~JEH\-Ez&`ѠXQt% 'Po  gFKOY"^0J|Ld4fٮdxΦ)_L 09GXn:%NB.Q"(UbbsA 6bǓW=yn!Se"[z]K[2QaR!B6t% w Y%2njzt'WB4X{Ob>0`_  T $uNBS'ubY~V &{5DD0>NvbP#KkgޠC3&%#rn`ԁ VZݨ\viIpն}埓Ldd-򋱖8 W}U%#+Wq+}yR_SURySHE"c׳^2{O]oa N]N62b{\+OtGJMAC62Dݤ_M#w_gf|Şn# ;&\dw22lpco miQ)4kr5-edHK.{X[@?(Fg\_g?foN)߰ 3Z_DIa7˹VW;S5i1Ilf@3ѕERϟ0֨2 C\_%x݅\ռD.7x-ݎMaR獻d/l&jmԮZL !bOϸoи^$ \>*mt hMzil?.*BV`(MB_5hL+8Z\iƉz>\&ȝ.?}4 })ˀ>bUYٽ(E/9W)%""ߋ=DSٓFzI 4 se%2IYfa_biSImT >1D/@&FX쁁`0,e 'J\i]W.SOD{^M2PCu6f?P䕦D+/Ob]BYU@/ǂgi HN_W \f60Թc$ׅFL gȘn-Ac <&<*DңTnж dVFxG;D.3~rkfyF*(/cw# /zƵ?URV5a\󷎌U2OTr^.bz ܍#%Tshց8zaT!ɧΤtQdŤhM"5 ?z(vwčHZثstDP+ +/Wl^6Q껡Uk|ܧy| 0/Ma;1]'<~{?ṟf}̱ O$WJ)m5ߑDojDcSf$i,EKZۋVSܾ-O2RˎW= dE ` vGg?ei]zVگzȞyHL/UU_3 n !բRu|Kxݘp[p0?,n3Zn?J?^x>)S7p2qE)CdW\@=%P.^L&b-gڗx"pSX49SGY厤0A,I(+8p lj Sn̨ jB5c:z&/];= B3*-kH{vxt*z<] VX(ŏ dwO@ UBRTוz~0L~m,ߒhW)iHrAӓM%C[zG>ᦒ?Jd, b\1Z9uDpL*i $[Ō-m KSbW13ulއxGt5`Bzb =@0u> tDY 6BxP~T^E~`(P~adZdoM=Cg] '[X{5܎X< B]v0bհ>?l tfE{3c6.j٬ٹ+T6AF5ִBqYH)7ؙk]]xBId XINHt` Rh5b%p.H?"9gfbiQ( ɤS YTy` ߳'ʘ!c|ǝp#2\xSp mKTfA,BD0P3&ntPkWl(f#z$1v9]j|4 Q;g`*uӧC!9cp{#x  Nc= >ٿځx؀'>Prݦ-5:=I{.T2#^h5"eƯ:ڲs7(e'ɖUۤ@;;Ȓ JQ(Q& LZzƋCtRQx|Ћ:^&xDb8׀,&G‚uNcR~}rI,I_LtYm]O -@$l s!Ow ĺ3_B1r0Ҥ"" 3u2(>u7킌m1 x7C2-4|DTGS P!MBR;rZ&M_w`Zp( nNw ׋QO͗2JAT ♛,~=@zƠݡc쒁4 V.¾i"9~jjsaѼGlSٶW?xrgs);!F'fA}ǰKU!7&rbx i>V[e>0Bδ ?F-OCU$njW`3h9DD`2ŪGVGJטguK/d?c +]\"6,əixGxő\cn ÿ=T D{wdwGۻQS._L"6fFX*;r,9~0#3<9kgs'Z|ĈǸh(zGo{)noy۶EOΪa,# ɆO!tqPX@'~N:Q],2"ӶS 0Wߡ8Dǧ%G{&Y[bsPAR3IhSл&dȬ}*4h RuvwWo2 ˾hLVb'Jsg1(@=MTM^>/#F [wwßI_Xe\MN,0k0xR/S}Rۻc!Hc$0P–g\t0|첢 t]#IwDvtJB[", idbv\~!b)&+^ZqNjrTv<0(D!Σ-˵9nϞ-$u4L2lPP.MNq7|ˁz[ #.x*D`Sdhcﯹ"WJ,( oɁ$uP |.Azca_-&נJ2"?u}RDFkQsbS}i Otl& gPQNͩo4$"2X=LtڏVHYPrw!Hp7l>>.)O9BH9(O#_hbքҾLbZzZQ1ĉ̱c6jLugʅyWAF?XbO]aC{@_A>QQT?ߝ`Y=ߊ=&{rA@͕ .!ޤ+d:rS3I>4jMf2;&l}PxQgk]ܨ'UG*czű7' eEilB`]H":NI95[#+ tt?_]J#+۠P 5q 4\J/͍` 50cʸiZ( `UeYc.\.yCe_pQt/W՗XR>Qd>6iPܠeifUPy_ڀ@o2IPIG%ޘMȘQ-f.HS$QŹG@7 )ј3,ʚ.(wq(vE#q8QZuۍj3e_&%*׵tAFY 륍qJk[F3mJRSmȣG~q|L7o![qt)K )YO##^/)PIg^UL+K:ui,yP4j^ְҊb}0 3I1jE% ҀK[-`,~.'~঳!ZQ2@/|mx|u+F{-eidL>MVɞn|2Qh0nʽc: X7a h Z<{X [9H/礫҃plV5 fIj a͑7{li@o4SZ"fþA ]Oϒ?Ja#rgݷRP2\ibovq^âuS]@e  dixg 6BIx`t<~l %KD,g$>Mii%5.ŪTSF2Epbƭ1ɰOT/pjCW=CB`SJ%ƞBd/SH>L.Y{l _xS .FtΖRw v xx9H{Oͧ%zx#8PjJqB8;]6!m*zRO/[o#Kemt&z";q;A-[N|tamb/A^`>d C"om+͡5eQlYpc]Iryvq9SZU("fW$'tEU74ʋ} v`GN2!WD >T[!FU["I_01.w,y (kO'쫲ĸsν8HdB5 kd^ɔiḪX}?3x/ :W ka©f<2"&POGĀsz4i_ےUxfu%SW5eD"wj#MfdHmdH$̈zC;hTٮ!wv K)6.\kbK[Mօى7{pOYʀj3*F]op\!yY!cx04RM2eЯVbzlj&υ'րQg\=>?9601jf'4Ex\2_"^ WHwT' &b,Zz뼞qrj˘Qt q)>-5SoO=Gtĸ+u9G:.iyި (Yc{F?z3t+Ydұ޿ ̬BGϢ'%6{EP ezI r3m=)-(ߓ@ciȯ-PT"8N8dD M+΂7dyƘCWb g}Ѷ\h9kclux i&b<*8p] >f{3zIT/48ٙSVz{ZV7deWLf5j%/א=LKW^`mA vYu^J8#S*NB2)20. -9 Ng >A hiz32K);.7#i{l{ \ʤpxُ1xևjD\@jp4GF3ҕSAHB+G~>v΂fzO|{0OfL}itΨp;a07++ߊW׾~lߠ(-zVv%%YDrҙКDqҥ*WpBT xfCAGnX9'h)g%˦L C%oIPy}vFa??د5hLkz%2[xGb4;EcbmE;\VsHN`l:`R 0olEYB.<&cҽ +M=J[[)G~z rV'/bI lE iшZA2eρ^ޑk)ZΣVO@k&l~CImR1BÄWJLhcEw:A|SgBwSu\+9H̠xNHDU͜c+no:-]2Ԑ k[&BF`0ԫgf.zg{]DtbW5gw/T!+Z\E÷`ڃ.s8ؽKw|I@HeHm|FStrp X2 pr6q!*?_.Z(u?9YϦ1EoݩK-83 @r %6,<:>'oWf UWs)`6(s7>HlR/9nP7Kp)inRWW%+jEO܇?׼OÂAUAX$_Og-Nm6_~ėg'}7)NF KjG Xs ]twܤu#7y(z2'Iޢ5E wLq7XXWR6pcDp6Ϡw!K,>NIj-PB )<7'rAֺ,2W*nlbO||:^vPFL̑m}G筅j]F|(Q |ƣe 7&:pXGяL;^eH@S>"p*4IoNkO'1{ 'm`b[ຢ&" 5n+1B`)Mn>*)e^BF,o (rw3%`|ZY- c'U;s RÛ<7VF:ڒ2b R(-=*ϾJw.[O-uϦ438Ϩ.6ۉuii~D'Av&\Ru P9 ǝD|A^}HӾnk3* PAgsGXĦP&L89c6+ڲ wSƜ ==nTV#NSCn ۂj3_S8 fxMNh5jzc 0_KB.%BR7;Gmb⺇>e\Khõt8\i@35 3,2^n\%|@ MXY7 lz|#v$33^|N^ 1d_,9qӼtkq_2/% B{Y:}u_bd0bśg[Q11;TnDMܻo3kn %''xlհK wh>mGu=xs9``eo2HG*=A!(3]xMd0 <]bfЉ|fFYyM&G%6I5Q@x13te_* ֖ȝd-CZ)G+tOZqޡk :aJU=3λLbQhNy >J,8(k2eWf PPЖF-Cf$XO5?0pU"[^l71˿Km))Gboc=.қI#MFGa؀~} 6%0^JWlCs!_ +YeK z]h!aw2ࢠ儴`_Iąe[mzsR-.re̦ h<>L.tںۦ -ʨ;Zbry*<\egB,ɕ͂jVha_hAiR5e=<YVt ``||{`3+yܼp,y ZtE~]Xt\._k^olEW)"|pqB3-jw޲Jq@^|aPe3Bޱ!Ә%AT?`y]I HcCl /7|i|TNl1o*@}>I/A\`q; {4K Ng"k|}koJ:XJRW-rUAMhL8Řb5+,!OScf L TN[GV~yFl&G3uH]EIX*݇#"-RZ#`BDŽ? 4H2 p#qE ͰGam3'A+'9䐐p߫=ʯTvwI;h:\T`eޖ`Nj|Ǣ7 K3I>FL4;lJeHJ\8FG-Ϗm bftĽր?(q;_~kMıҎ]{Oݓ*"}v{>̱gx,0& +Ki"e!!Y3|GD1QcR3m9o\BK7;9VQՠ5̋b=lr RH [*Yy޸N/5*6Z*A> stream xڴeX[Ҷw@%7ww nB/=hΪZU]ֺ4 )(sf`adɁ@, F6&VFffvx QG%N r(8t23S$v@w) t6RE3ӻhgniy_" p4pO?E2F& 7'kK)@Q r{7ZAvcdPjTĕU j*4U\AETEUM &,*$TTUڽ7ȫy\N\UXUKQ3X@G'?eG2ޗ9l*pvebrssc4wqrf93O r:m5%'YK" п|_nwF8ip_e,Z+( 5sٙ:98 M%uqtSC.txٹ8y7& ;'K'ge,m;3Klr* g z_ ɾ"7>v [wN'f'gc635wS{&5;Knb_/{= ci|r2r]>^t7pL-M] ]ɿ]7S3j A@)Z.66F@ZxWDhH9IhhlbWeW%a;s _?'}t߯?׻|Sibmtrpdo& -UEL@vVN<0rpXާ״@K.>3#}$/0LIo0YL{7^o0XŘYL ;?N@D,ﺬllFweb{dbk爘˻`?]?]?]?=?w.wwn#X*gO0{/VqvY5,M3rvta~X?O P}c݋l6翖 ם~hg4_[[V@S0Ua j$@ev(ARz)vX6ړgnL> O3fɭWj}aH Mta~CJ%խ,v+YditAw_E&Xw~m4 F`X=!U.PUL/ N #6c4:k5'ڦە+w,Ag :^'w)|i@2QFG){(CsO!SfEWhօq)eKEΡ{V@LRcc]YX6 MlUMfIvy*`DAf&JLRv*ST%.v|XN(*_}s TX#3 x1-7hѯ0\*BN'S۾XK.TgplnJy.x) )4r2Kݱ9VȚMs<5_]I6 ~-]+ I(ԍyQx3F~BAF ۵[qj =ՎIξSnp_)2"{ LyS^c69+9ӈ"9 Hv޽b3!:jFr8 +$U%2Lm{r?^$=ѝ)?;a(d~O[w"Ue:kLRyh}0G"9LSpb{Y+QiZӖtl V<`vAE%CTFlKT:F2^aWMx)KzNb8kQzgp?&|Qy].P38,I"7[.62S<<"*l #x&3M6bIčtYLf~7uCSJJ:[c8^_rSǓAt4Q;&;K*VYz>XpvI#E=/HT'=FL:؏'di>y8"% <&(3`FM>bC ޣ*EGTB(s-*׆ܐwA%B#+i"z ֓?7 |w 5jsهXl/;4-SL[kLEx4\K ep)>:"gӾ-2F(y{1 7eح ^[._y q@l&i_+8x`v%L0ϐ̦^.@Aaq!8A`dx[5bGAwA87<".;š*~Ml}A M4$Ҩ N~z7b^c_e`d"S f1 MYTR-$!>P)]xC|_Rʢv̞>"h޺xpZp>J>">@m FL;<:v[,}>9e6~- ?gMNww˵xc-W*(]ce`6":j nRfعuOݘmΥyΤG+_[ ;(fhH>(+Za[{i\<|Ii-YM–|nG^Иl3Gw\ j47i'CJ\|Q>Yu7?%&>7#Ga {r@Έѧ_@chpjp1Izep+/n~ ԈڔN0e fQ#]Sޥ>Ԛi'1q*$Ij[_0w1i97{Etj-a\޲%HPu׸TYc۞H\ݰ$ MgZ0\1|Mb7pIƣ +hM^F*tYr~iUfWӽt6߯L|Inz7W#J ܚTL;U}BX_SvaLfӾP>B}hg08\e+Cd*}t`>rZ#o`jm/,nY#qǎ%z7GbRlĠ0/6:z꧞jytʨGe ExP%z:A&h#D8qYuԅ_ /FHW~DO39x=y&55&UF4p r||3_\)yX)Jů' +{؟ B]mH3/*ٞQ*K+BD8 7mY!B+#~x@ B) u}[31NU/żS>q˻+M{EsNJy0?}屪e@\UQhyLrX5m1>||*аM)T-F/[oL.>V'9{U&d J;Uu#vp;J *`fFzS. rR( F5o Mo9Mʹj xZ+\VG }ƣ;DOrsf\8 r & kjg(XNI7_0Ō(tJ"mOEHdav )XUW!?>qigW(@{\dpQ9g7G5 rS#pcf׿>ZiS; [2?, `%p>f <{<q0B߆ڷJGwaBR9[p4xfE85t!$TmNWvQˬXw,ݥMq7:Uj(4r6B_ْ}K "Ր40uLKIe~s)cHWFcwA=Bi.p('$ ܩ;fGA+tXF<$LXE賛xв?ƣw?:>鐚HvhtS~?gw1V h,YTsH˘<!Az*BR9fܾ CiF>5uڒ(Ǔ[2Vlyy*[+1ʖIԣ.>L䶸Β!аӔr*y-O`R]my a9׽9(t1Ag[z$B't >La8VHPyEn},p~r~xShmН` MP2eFLWG$#{Fg_(a(@U2$t̲8LwUEc?g?"+)ڿI֪,~`FxT fo@X:^Dݫq8ѧ4L˞~nhrF{T uQ=&LW箳xR*WhzQ=R,71bς嘁vʄ~A'ZP0\+oi96'@\[^Jlط۵[,UXՅցoHWՎ-n˺Vyj5+ Ũi[2zZivm@d&K7ٞ: 桽3;9srb֨?jsD959+Ah Ɗ4@F}Escu3d93Ɠiw5ysةE^E{j ^rnHhbj@v~E>o[va.-O2ܣLH}/m6Ghl.E3ю/,*2e8.i纏V\1çɧڅ|88W%[ }]#\5OfڄMZ?J40t+`Ox8LFJ%/E»v_.#rw5 rGܨEt m5.c V})`=P ȳE-1 VO_G&m0c;t/OZ̈cv/f98ڕ]jpAq45gX+M8Aϡ i}\fQFsj:2< 8T>Xwhα0V]P!CQZ1{!@W)j*y%5R'YqT qf񅸔O@w}/mHǺ6OQL"?ZX)oLUfȤ~CȜ_.+̓ZRçl֓7뗝Sݟb.F0cPK>,RloUqs7 ~˝ V7c "m^{ʲXd&/8RZtM }RUk4m1DɆhS..*)T"lgvԵTU˟Đjȯ iNU4(%N㯏lmr E<0VU.3xQSAU Ϻ<[ŵ3p)e4n7`. }xt=bǻ3ɐ&mN>D0uXp鿩{֚`Dfjaqx\Bq2ςc1O #)la,1W laRV5-l7#@w 0RzFmUZ?~4I$$is'إk3,#8tEۅ y"+tzXhJx$~Z#[r}@ ?`XvhσBTg"dB>fl%΃ ٳsBa:N}vp[*+blf;ܕ{aC@_m_x.fIMJ.UN3<3Pӈ  _Ff9M2jvɂg L]JTAJnS9jC>E:JAp/k.t `eb`}5AQx.nR0?@֩Sc+z}nDs'p"|nG.C7%n?~u{ R햮U:Ў-na X|H&=?k-h nz-ʺ bӋ>WzU71mR!Ҿ7N.ɧnO> '1(;ٛQ}ޔb׹; aWUC @d xpF [6|mv)CRq3H2fNjvzj&CS>_\D$ <0{89rKCthW:Wr5m})^<*x!B#Q1RغXRΈZ;2E%츈6\C$r4jrnkQdDa+]p4JEqsPl9 v V Z\1Jx7 O\g7ϭ?lU?qƌt$[T/ՄxTkD8 \Pu^$χ]d/oi?`*5=_~d^噚1~tORy#9muV\)'ǜnC S>T!pnocޙ1cd|ԒV`dc0BFtY,]Ț>LMוqA{\R49 #犃cm & D3I,`qHUdjsGh(SS tTƮt~,=u}f.5iv4uC}>h,<<,ٱ,DMim7T|$Zmk3rĕTT֧0ٌ&aXS"s73l@E]DH:2lᗎ87ZGKȲ7ӸCeʧJHˉ\.ՉRM#.m1hEDg<c8kar9x/4FMcrжQ9lJᒾv[?BU` nJKT9 [ x!.-6>'&P5}}Qzp2p Pð0+n{+YK2Ro>Ӏ?8PZyJح %ʬ-!0Yyb{* ]?漯^-p  VoԿ@ʎ"oڡ ڏ4UOd2 Md0$Z(u+eC(<;`|-{JEm/JzN]qT [0/*{L J!?DПJ ׆)=~%h"M i }|,5|JzؤKWhqڏ)sVRjכ"4m湁kYwISiNaMoُctp buZ_{Ȟ)NnewxRaĽ"۱n\wpiYMSie}g#-ܿI+]C'Ħ%  3ych;!٫wPgtk;0ȱRʥ3azBTDAM'GLb&WIw>-Տy}7lng.р?;={F"^o-/;Eг}\S zPbɥ+^;[VnnO]Ê~n!xz"cwJ: 24FP^:Hj}cQ$px#藺Sf!h)$bi 1x^{M830;w9q_wf_P_+ !O"=3|%AL8+nZɘQ⯱-*Ro=& 3݁/n׵+2#Lq5tȣPqX0yvJ7(ePL9̗ee А#SoD:.Ҧd)~G(6}hYsWj,тGw'.|m_3s.Q.>7E`d|>Iȓvn#ڕSZF(.;lf G{'z@YYXb`Mwi #o\Tj|eD%FqyٮFi=ퟓ6  >25qˈJܯJwkb{Lh$kjW)2=.l%'# ׫XyT|oMT$M>.jE|"8#3?38Zܷ@6!uH|BQG>wqz-I\5S"yw8uZ/]I ԓC(Y3t&2Q{BS EJL0?7L`@_w] }ZiV )64ⷸ K;!Z/`4!\Y)뒁Lhw}&IFRoaJŏ#Qw-_PHP][ɥ┐3~*1@HQ_kEVg),%ly=.8yX˝c5gk F;nm87)qw ЕGz3}Bٴ^I@~0.$[US6bn8  ?Oh&C',mI]lsx`3MI>ц@J)eѱ/P$Dhk\b:|HstULLy,p*4BxE3$T$M q6c)JbQxZjU>`wy=};[[GB[p,c~`Pː7eJ kk041c!vl2w熠`SBa7gGb qnAL[RQKJyV>hA:nVVب~&Qb*lpj/E ԃYI &h!aq`3*h|?F&`fnpeW7#PZyl$],c̃_oBdz=uxL {מv]eVi*@|۩I+34l ^ogNLHz`{3sS^MXvU(}Y7-iƫmLm$/zEQ h_t k4b9xj~ۃ"u~UX.颣vmHl6Q+ I S&`^P?:a!̦"\.[Fxf<f+҈0~Gd.pfQ`|uD"~ON#j:i X3Z(% ~IEAcڰ/Si_X㲭HO.5VB}0rj/vzHir`Of4rFx d"D`o:043XViL:oPIJ&EFT+0v%)9G[Wo6 Ŏe,thd=NdB>k3g Ro\ adȢOo.+d$^1 l$]{7iO6E[XHoq Wތ@C+z5>Ȥ"37a8zTİSsE[pʧעGK?Ghg"r6SzyyI-lIC*bsKGBUT+Xx-$e٧ݡbμhDX`tZC?x\I?Cnzb\!u_yD5IQ,?"Q"fgM1r 2&,;`>kS|Fsq\ZႥ"jkoFbX|bZ9k1yКH%|ofTP,gВ61n-$&y)C]rfL .FmZ¦ò6žOQn[1L1'˥ +;u_GYJ={jlZ>p]\'b*hY}bC9u{uDa{֞qLN4#ZFݍ&-qeH|E4$W(asV~ۏgT? Ob{j)Lbi=`|E!9@5y孜sDoL\cw>JftK隿uc绶fw+^ D=TW42&j1+=i~utzV u;-8 Bt9d)xĬfC4rʗG>_b6sI,$wb~Nb&Π/u?-A=$xg̓9ȶ$ p46)ʷ4qPJ+&>kHaكqDBVS+!\'l3ȭUZW.n<F?vqP}B[-J=2#6[d?xt~)Y3;DYpVZ!mi +1YExwOۄkH:ܮnӶCmg/&tQiU$pX7+x7[!'m @]m}vQ~;ٞGXX~p$ K-s_n>NMcΚiys|ĶCE_)^|촶YYu80C?rDG0e3DAjm4l1AjY)kwk1,}? v(9›{n[[`0Āڶ#lԓ Cq* k<4ty2s"|ʅ㱺L `/hrD9`^>Ǻ۰wV.x6;#r OB|oIBIjlO-3'wN3*4d{pG7&3g ~'u$;]W^8wNdoBYI2KQ)fgrC=G9 ENq:5 2LѺb)D01R"=aWlXTo Qc4_[8=?(t GILBڎzUpPGI ݺqs(Qp9:Gb!49d;*`gZ44q]]cK}Ml-]]2fZ]V?>s\ȕ*$8К=$Tl qEɿ܇RKA`yP )譂Hg2׵CXʟOBPE P#0Uyi2sEVq 8!ٞnFKHqPjЬ*Pέr,s쇧Ҧ[ , )+0\^AG7M8b!=t*8P|Q>c?O.'I [&|7eMq7S߰ALzit>~~ qמDnVL{sH[hQ~rko"@6DY#*vlH̪OrYvsOeKoFCaۈTU^0#<0=mS ]O=)$͑qN+qK%dnA+ekxEY$CBdgڏAD[z%ՍJBNpQՐ`:J?ddh5@QO1'^Y3$H|bl[#hx+;Fđ= .g18w$IqO!~N퍯:v#Otȳԭ!rG΃Jճ.3~N#):WR7oξX|ߣ7D3"Lh/kT%h[c%F.R,4Jg/NZw2Tֈވl%pOgfN9Ȧ|^ ( DŗgՌo}}3>Ń0@=uo`6H+!!bwܢ ’s&MG. \|I?k^wF%RͲ7+n\'77 [P>chXŴ1Vj |ا`kE"2ЖM[{Yq[d&[_ Gx㵂&y_- ub{Q$9@ |[߰Ꭺ=m l ~)Ԭ;l$SOVna0z&#u*vO"W,lza=hFhDҎ s$6OC On(i~r&;f/?oKGnC#Ob3f=}5Oj44 i}h| E|5m'?7w9NzmfU½oCJ L 5' K@~kGS4'% JqwТs^E6j]ޗQL!e]"ҽwiHb,7 oc тF,qS'&U([?JsH ,a*I/oi)I/ gjK1qܧ!d}?;MF{Vd˩$k%Aq`"JokH1bzc("jbTPU%vUBHTߌǛ~Z@n{yY ?slL9SXH8 |¯鳄^Z #z8Q28!0!5rH \U3 T<},j58⃳6l#< uA־-h4 c%[;g1 u[R$lj8M K H!nN:)Y6@CgnB`=ۑ %2gl5RVێ`z \݀ 695 $eԆ YioUv5tR/Ån3\8"/ħ0n@ԏ#bPzryDj*ٸ-R8O9EIĚ߹:Y4x>E܌mh)HuS9|3aX^.Iမs p\Id4?Rd ѐx>O?Xɣo)zh :Hzx~UV&4K |Y=O^i~yw5rȞKB8$X\'"bܞe5i&8ե($:H =ATS|JKd:r$S -ޯs|ɵp-d-\uK|#*[4cɍH$^a$ K8Q:sc嬠o{mWԞ_ч'ud(^9jmS|9 7srq궻H=$N]28bMϟA2v55vf3h0,3$h7F_];XFت6ftzHĭU !P)8qfrwLflfs:}o <}Lȓh@~$$kۜjrb]sœ^ևxп9>]lu7+ j]~c `R3S Hyw~fX)$ 77C;ϳTq#f.~+0-a&~O_R]'_.QvޯzhZWok4?+lI^-qTL%Mh"95uDݤgjL2۝jSe*<2ssY,M`/ZǴ@f;K x]mp᧲Őz9XJɞWf@PQ:amm eY|NjĺtFﶶ" _ "|]y֎d[_D^-}I`*4A̺K9F$?SG^l2VLGw5I#'=_V2oH!:> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qDŽ r Gէn8A{,쏘LEvDB``B9zK~;_q`>Wgy o.>ݫﭯAbZ%?6G_Nzy;9ڰoiܰ^]0zu\~3ݍܥ: ل0%1 " 0Z{q́0R0r0QK5<T`,if,1gT Hӆp1X:,p8}u 8alSM3?r>x\i"EܰpJMkl4\?ǚc:#?^YHwuprQF^odž1BЖEQ?1^׆ƨАԗ039+ãbLi~jЙ}s~zrCOe fYJ|֟uМ8gΈrY׆}ŊϘъ1LҊkgigϘ݊og3f3|3ߊY[3 =L3f/gd ,' f)Rx jb&'W *.MGZN(:p~7a?}]TyԟE}Ư%Vu'e% endstream endobj 106 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3FXҝA5(O)suߖcHQIܮQW Lڮ9ˊ6nK5NoغWi~r<d(Vu;_=85vѩֆu5CNmm悥+U=#)\][|, MHS"#p #>y| #:##0)%T\`YQqJƚ`ci|1Mَbo4m `2WQ/cW888sέ-./qJ;&\ k(d?F#h0\?Ipa]~9Vk?q1Bx.BzҬÀhƘ'g 2xk=6u2,bق6E0F,eL燆LY` YecODV3Μ蛳;zr֟P.O0{S3ux9(uF: }6,V|ƌV|gegV|F_+>O+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z endstream endobj 108 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z6 endstream endobj 109 0 obj << /Length 843 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N7R!ݪ70W?g_,ɝиYs{ ]7;׺v=ߩǡoݨM'opiT}IAu~\3;he?<{Q%(SVk-#&9sQ擾ݾk^!00j(+m$?Gwt>X.oTuþ{S_tpСtZ|I1?H/'BZV;ݛ ZԲW/{FR^ww?U4H6!L@@B@q\s *G|F/+>㹴3Z~Z83f3[:٭ ߬Lg3t33 ~!>CO!>S 33>IY ?BXIAup*Çq G潪N$p|eO_:q;:'dE_kCa endstream endobj 110 0 obj << /Length 845 /Filter /FlateDecode >> stream xuUMo@+H.ȲrhQի C}͌6jo73o{q3fѭVO4cpuU sk/wOwquy_t}??p]AAu~\33cA}P>>%t;en>r8`S0Aj~vUk&Yos yv rOiHM0[7v,ܜǽJnkz~lNͿvt*amкq۸qۿ`J-ztH]{O|, MHS"#p #>y| #:##0)%T\`YQqJƚ`c2U{;5Ҵ!\,18"\aD E_sN[sS9)9^W$js7 GZ ׏p$uX}/S/w"': fyRy(#c^g!ch"ƨ-kC^d cRx~h K^| МQV14Nd5cY9Y?C9돡'g ?%>O:ShYggΈrYgDg>[bghX|&^V|{ig33qgng3tZ[Yog,g-g B|B|\3gg3?f)O5[TT+&GUP#a#7q/c?z~#袳rdbP)n endstream endobj 111 0 obj << /Length 700 /Filter /FlateDecode >> stream xuTMo0+J!m0U !mTto4j{zv|tv ںQf|6'op݅uM{}ugfci"Amƃ}>,%rtPRJ(:X'Ab~oںT7h uSӌ]Acq`sy̟M.n? D`އщ7+d~4Wj7vw VRŪ,ׁk/bxO0+,F )1!Pp #]QxQTv)#ZBYLt/X^r<1u%pr_d9٢PSi0@WQ_Uh֩h諵"qFM]RrCpt39Âж~j3Fezp888Q:1bc7~}Hq('bĄ>^m# &zd}4)` "H,4%!%AQ߄B[B~)ҙ́ _)M?DM;豬;kyoQnNRd\Ӎ;WA} zoZZgbT$Z|U endstream endobj 120 0 obj << /Producer (pdfTeX-1.40.24) /Author(\376\377\000S\000u\000s\000a\000n\000n\000a\000\040\000M\000a\000r\000q\000u\000e\000z\000\040\000\046\000\040\000J\000u\000l\000i\000a\000n\000\040\000Q\000.\000\040\000Z\000h\000o\000u)/Title(\376\377\000S\000h\000a\000z\000a\000m\000:\000\040\000M\000u\000t\000a\000t\000i\000o\000n\000\040\000a\000n\000a\000l\000y\000s\000i\000s)/Subject()/Creator(\376\377\000L\000a\000T\000e\000X\000\040\000v\000i\000a\000\040\000p\000a\000n\000d\000o\000c)/Keywords() /CreationDate (D:20231002185740+02'00') /ModDate (D:20231002185740+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/CVE-2023-32700 patched) kpathsea version 6.3.4) >> endobj 2 0 obj << /Type /ObjStm /N 92 /First 745 /Length 3610 /Filter /FlateDecode >> stream x[[۶~ׯՎFNguqbǎ8Z6c;EڕbәA .|bJ0̈́0J0--C+L,I-Ye&)؎DʜɜSL*JKMʤLg1Z~B)kB[9Ag)05a V*Ղ)U(V3Z^$$0Ĉ1ᚚ!2a&P m ;"Kc ##p: P6P@$WtHdʀ t&T, ~F: XĀab1htfRɑ%`/M1)X$ 0T."JњP%!ˠ`4'MKx5`% %dBhcF)@QJ ) Dnf 9hPK`[Eͦ?+JƟ Ӣ9gG:gu6G\5YJt#&M5kxϊIё\0_ lڰF >i4Әm=f{<1(Wb9x5DbJ+2E ňE(qE!QĠ4B=.鋱ן~YĆCH6*x$b8*rșǩt,#THAVGy U"=:ELzNimӎ vN!iȮ¶udoձ]ӷ9$c{A^K'?v8*BJ>@*ȏfM=–#1cgEWŴf6zӌF4hTG*vC^R%IpVRu74VrVt'*??9!"[ǫ ?-./s@Eb;0|Y6_lE~JY *lir>-fy?Xr̦UULɦSX ,Q5f%6tEu^mV,٦\iFu6ayǼvuIN~c?'e^|Il Z,h\Z\^v>P!2Kg.{$se2We.#dQigۨRinCzo-A8)fL[:E=4q$ك)z 5o-sQږD1ԡNcw{VFrcbH&{O(1(S"NG*vs[*?'b-oXmka>u|؛h rGk 3*PE:[FJ2`P(mKHJ(3u:Pڷub7ZKuB()ឦ@Z̦%:Q/3kFu@gG|NzЬ$F=-LOm5 bCNi{tB+eUVmЛRzU6kzO /)[s:>ȩԎS-|NI\K>cDVڇl1T0T(XG('xfe7QcK']äU9lHc; Vj2%ٍ{}ArD; c[# †z(z0lUc"N5{P # $HnV-Dh9@ WD[~v[$qLuB/SC)cE+G:<ց"h)WIFKд~3X?('P z[{*MYgٻw~/ߔl%I`keD:wSŁ$[I Dh=oE1}/r&_N0NvWYEߏ 3~|s~/ >W +^5W O ,Kېxo:$ɛ|Yd0T 0RĚNw@PH (1 S?/w53#,y\\.-f ~Yn*էUup  *oB!_",Z.>zWIsU9o(w?Ho|Y]IJX:WM=6&栭ue~M%bP=n@_?۳!{ B11} &>m"BPL>Bq@'k!{wϠrUFm ENi7;u7WP_xnv[o[64߰x/6/pn=<u޹]/8CpVQyw gN;w/;v/^|[C)pS ;$/gzΈz2ýK?PI#hws^l !?;-!cE6wŬYvH{V3r1A*ɚ\d.e=rǰ'ۗUzdJ k + {w}Y5`!'pw5}QŖ2b˙E4xRk+6. 0tt]֤zc_eTPIMsqy8=6nI/ތWfnO"}fR>O}y]k>|ajjݱ1Vk;>sYnE=hդ\Tzׇ=*L z{egדǬWBU>]>jxGܺ,,k1m[O5bY4{Zm5jLqcƮmWK]ێݝz_j}k"=[Lj!lMI,V$S0[l1YW%nOSNf%Big1dOɽS>DNkr̨_hm$^](磐vkrz'D K^Cӫuzc]F endstream endobj 121 0 obj << /Type /XRef /Index [0 122] /Size 122 /W [1 3 1] /Root 119 0 R /Info 120 0 R /ID [<6171E55371AB7188A0BEE37F2BF111DA> <6171E55371AB7188A0BEE37F2BF111DA>] /Length 312 /Filter /FlateDecode >> stream x%ҷJas>c/{4Xc%''W-(BA (8xNˏ""#꼤@x  Uk,Ȇ5XXC.A6  ` H%nxUJ-@JaJR(rPi[jU>lS ]YjFf u@'@+Aҥm0x0!8,,$a B0 3*'vmVҜjҼj2miARXݢ:>ܝ% select(sequence_id, starts_with("mu_count_")) %>% head(n=4) # Calculate R and S mutation frequencies db_obs <- observedMutations(db_obs, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=TRUE, nproc=1) # Show new mutation frequency columns db_obs %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ## ----eval=TRUE---------------------------------------------------------------- # Calculate combined R and S mutation frequencies db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=TRUE, combine=TRUE, nproc=1) # Show new mutation frequency columns db_obs %>% select(sequence_id, starts_with("mu_freq")) %>% head(n=4) ## ----eval=TRUE, warning=FALSE------------------------------------------------- g1 <- ggplot(db_obs, aes(x=c_call, y=mu_freq, fill=c_call)) + geom_boxplot() + labs(title = "Total mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() plot(g1) ## ----eval=TRUE---------------------------------------------------------------- # Calculate R and S mutation counts for individual CDRs and FWRs db_obs_v <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V_BY_REGIONS, frequency=FALSE, nproc=1) # Show new FWR mutation columns db_obs_v %>% select(sequence_id, starts_with("mu_count_fwr")) %>% head(n=4) # Calculate aggregate CDR and FWR V-segment R and S mutation frequencies db_obs_v <- observedMutations(db_obs_v, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, frequency=TRUE, nproc=1) # Show new CDR and FWR mutation frequency columns db_obs_v %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ## ----eval=TRUE, warning=FALSE------------------------------------------------- g2 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_s, fill=c_call)) + geom_boxplot() + labs(title = "CDR silent mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() g3 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_r, fill=c_call)) + geom_boxplot() + labs(title = "CDR replacement mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() alakazam::gridPlot(g2, g3, ncol=2) ## ----eval=TRUE---------------------------------------------------------------- # Calculate charge mutation frequency for the full sequence db_obs_ch <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, mutationDefinition=CHARGE_MUTATIONS, frequency=TRUE, nproc=1) # Show new charge mutation frequency columns db_obs_ch %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ## ----eval=TRUE, warning=FALSE------------------------------------------------- g4 <- ggplot(db_obs_ch, aes(x=c_call, y=mu_freq_seq_r, fill=c_call)) + geom_boxplot() + labs(title="Charge replacement mutations", x="Isotype", y="Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() plot(g4) shazam/inst/doc/Targeting-Vignette.R0000644000176200001440000000613214506573215017123 0ustar liggesusers## ----eval=TRUE, warning=FALSE, message=FALSE---------------------------------- # Import required packages library(shazam) # Load example data data(ExampleDb, package="alakazam") # Subset to IGHG for faster usage demonstration db <- subset(ExampleDb, c_call == "IGHG") ## ----eval=FALSE--------------------------------------------------------------- # # Create substitution model using silent mutations # sub_model <- createSubstitutionMatrix(db, model="s", # sequenceColumn="sequence_alignment", # germlineColumn="germline_alignment_d_mask", # vCallColumn="v_call") ## ----eval=FALSE--------------------------------------------------------------- # # Create mutability model using silent mutations # mut_model <- createMutabilityMatrix(db, sub_model, model="s", # sequenceColumn="sequence_alignment", # germlineColumn="germline_alignment_d_mask", # vCallColumn="v_call") ## ----eval=FALSE--------------------------------------------------------------- # # Number of silent mutations used for estimating 5-mer mutabilities # mut_model@numMutS # # Number of replacement mutations used for estimating 5-mer mutabilities # mut_model@numMutR # # Mutability and source as a data.frame # head(as.data.frame(mut_model)) ## ----eval=FALSE--------------------------------------------------------------- # # Extend models to include ambiguous 5-mers # sub_model <- extendSubstitutionMatrix(sub_model) # mut_model <- extendMutabilityMatrix(mut_model) ## ----eval=FALSE--------------------------------------------------------------- # # Create targeting model matrix from substitution and mutability models # tar_matrix <- createTargetingMatrix(sub_model, mut_model) ## ----eval=TRUE, warning=FALSE------------------------------------------------- # Collapse sequences into clonal consensus clone_db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", nproc=1) # Create targeting model in one step using only silent mutations # Use consensus sequence input and germline columns model <- createTargetingModel(clone_db, model="s", sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", vCallColumn="v_call") ## ----eval=TRUE, warning=FALSE, fig.width=7, fig.height=7.5-------------------- # Generate hedgehog plot of mutability model plotMutability(model, nucleotides="A", style="hedgehog") plotMutability(model, nucleotides="C", style="hedgehog") ## ----eval=TRUE, warning=FALSE, fig.width=7, fig.height=4.5-------------------- # Generate bar plot of mutability model plotMutability(model, nucleotides="G", style="bar") plotMutability(model, nucleotides="T", style="bar") ## ----eval=TRUE, warning=FALSE------------------------------------------------- # Calculate distance matrix dist <- calcTargetingDistance(model) shazam/inst/doc/Mutation-Vignette.Rmd0000644000176200001440000002307714367147774017343 0ustar liggesusers--- title: 'Shazam: Mutation analysis' author: "Susanna Marquez & Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4.5 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4.5 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Mutation analysis} %\usepackage[utf8]{inputenc} --- Basic mutational load calculations are provided by the `observedMutations` function. `observedMutations` provides multiple options to control how mutations are calculated. Mutations can be calculated as either counts or frequencies, divided into replacement (R) and silent (S) mutations, and subset into FWR and CDR specific mutations. Additionally, alternative mutational definitions may be considered based on the physicochemical properties of translated codons. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. Analyzing mutations requires the following fields (columns) to be present in the table: * `sequence_alignment` * `germline_alignment_d_mask` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(dplyr) library(ggplot2) library(shazam) # Load and subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d") ``` ## Calculate the counts and frequencies of mutations over the entire sequence When calling `observedMutations` with `regionDefinition=NULL`, the entire input sequence (`sequenceColumn`) is compared to the germline sequence (`germlineColumn`) to identify R and S mutations. If `frequency=TRUE`, the number of mutations is expressed as the frequency of mutations over the total number of positions that are non-N in both the input and the germline sequences. In the example below, the counts (`frequency=FALSE` ) and frequencies (`frequency=TRUE`) of R and S mutations are calculated separately. New columns containing mutation counts are appended to the input data.frame with names in the form `mu_count__`. Mutation frequencies appear in new columns named `mu_freq__`. ```{r, eval=TRUE} # Calculate R and S mutation counts db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=FALSE, nproc=1) # Show new mutation count columns db_obs %>% select(sequence_id, starts_with("mu_count_")) %>% head(n=4) # Calculate R and S mutation frequencies db_obs <- observedMutations(db_obs, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=TRUE, nproc=1) # Show new mutation frequency columns db_obs %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ``` Specifying the `combine=TRUE` argument will aggregate all mutation columns into a single value. ```{r, eval=TRUE} # Calculate combined R and S mutation frequencies db_obs <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, frequency=TRUE, combine=TRUE, nproc=1) # Show new mutation frequency columns db_obs %>% select(sequence_id, starts_with("mu_freq")) %>% head(n=4) ``` We can plot the mutation frequencies and explore differences between samples or isotypes. ```{r, eval=TRUE, warning=FALSE} g1 <- ggplot(db_obs, aes(x=c_call, y=mu_freq, fill=c_call)) + geom_boxplot() + labs(title = "Total mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() plot(g1) ``` ## Calculate mutations within subregions To restrict the mutational analysis to a particular area in the sequence, the `regionDefinition` argument needs to be assigned a `RegionDefinition` object, which simply defines the subregion boundaries of the Ig sequence. For convenience, `shazam` provides a set of such objects, for which an overview is provided via `?IMGT_SCHEMES`. Each of these objects cover the IMGT numbered V segment up to nucleotide position 312. Different objects treat regions within the V segment with varying granularity: * `IMGT_V_BY_CODONS`: treats each codon, from codon 1 to codon 104, as a distinct region; * `IMGT_V_BY_REGIONS`: defines regions to be CDR1, CDR2, FWR1, FWR2 and FWR3; * `IMGT_V`: defines regions to be either CDR or FWR; * `IMGT_V_BY_SEGMENTS`: provides no subdivisions and treats the entire V segment as a single region. * `IMGT_VDJ`: All regions, including CDR3 and FWR4, grouped as either CDR or FWR. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. * `IMGT_VDJ_BY_REGIONS`: CDR1, CDR2, CDR3, FWR1, FWR, FWR3 and FWR4 regions treated as individual regions. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. When supplying one of these objects to `regionDefinition`, and with `combined=FALSE`, the resultant mutation counts/frequencies will be tabulated in a way consistent with the granularity of the object's region definition. For example, * With `IMGT_V_BY_REGIONS`, mutation frequencies will be reported in columns `mu_freq_cdr1_r`, `mu_freq_cdr1_s`, `mu_freq_cdr2_r`, `mu_freq_cdr2_s`, `mu_freq_fwr1_r`, `mu_freq_fwr1_s`, `mu_freq_fwr2_r`, `mu_freq_fwr2_s`, `mu_freq_fwr3_r`, and `mu_freq_fwr3_s`. * With `IMGT_V`, mutation frequencies will be reported in columns `mu_freq_cdr_r`, `mu_freq_cdr_s`, `mu_freq_fwr_r`, and `mu_freq_fwr_s`. * With `IMGT_V_BY_SEGMENTS`, mutation frequencies will be reported in columns `mu_freq_v_r`, and `mu_freq_v_s`. In the following example, we will explore the mutation frequency in the V-segment using two of the region definitions. ```{r, eval=TRUE} # Calculate R and S mutation counts for individual CDRs and FWRs db_obs_v <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V_BY_REGIONS, frequency=FALSE, nproc=1) # Show new FWR mutation columns db_obs_v %>% select(sequence_id, starts_with("mu_count_fwr")) %>% head(n=4) # Calculate aggregate CDR and FWR V-segment R and S mutation frequencies db_obs_v <- observedMutations(db_obs_v, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, frequency=TRUE, nproc=1) # Show new CDR and FWR mutation frequency columns db_obs_v %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ``` Plot a comparison between CDR silent and replacement mutations. ```{r, eval=TRUE, warning=FALSE} g2 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_s, fill=c_call)) + geom_boxplot() + labs(title = "CDR silent mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() g3 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_r, fill=c_call)) + geom_boxplot() + labs(title = "CDR replacement mutations", x = "Isotype", y = "Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() alakazam::gridPlot(g2, g3, ncol=2) ``` ## Use amino acid physicochemical properties to define mutations By default, replacement and silent mutations are determined by exact amino acid identity; this can be changed by setting the `mutationDefinition` argument. For convenience, `shazam` provides a set of `MutationDefinition` objects defining changes in amino acid charge, hydrophobicity, polarity and volume. In the following example, replacement mutations are defined as amino acid changes that lead to a change in charge (`mutationDefinition=CHARGE_MUTATIONS`). Mutations that do not alter the charge classification of a translated codon will be considered silent mutations. ```{r, eval=TRUE} # Calculate charge mutation frequency for the full sequence db_obs_ch <- observedMutations(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=NULL, mutationDefinition=CHARGE_MUTATIONS, frequency=TRUE, nproc=1) # Show new charge mutation frequency columns db_obs_ch %>% select(sequence_id, starts_with("mu_freq_")) %>% head(n=4) ``` We can make a plot to visualize if mutations that change the sequence charge are more frequent in one isotype. ```{r, eval=TRUE, warning=FALSE} g4 <- ggplot(db_obs_ch, aes(x=c_call, y=mu_freq_seq_r, fill=c_call)) + geom_boxplot() + labs(title="Charge replacement mutations", x="Isotype", y="Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS, limits=force) + theme_bw() plot(g4) ```shazam/inst/doc/Baseline-Vignette.pdf0000644000176200001440000077511514506573157017314 0ustar liggesusers%PDF-1.5 % 11 0 obj << /Length 1771 /Filter /FlateDecode >> stream xXm6ίЁqf,2w7|(tnDɹ%뻫I mڻ}zvy>>1OstD80 sp޺gM>׭*&^6;.jҲL%rIZgz+]d*\=g>w`IKS3!Bg*#V}Q`;j4$yj m.K&I,\U,tE:]hp,hTYkF.=t/=H0OJ㞜 >~HAxPa-y@_a|0Rd{%w $C]VĴHmef6&r#iLC]e*5ɌYM >. c-wYD %8 =4HЇuEd8i'=$6E|M:]TP.h+zY9TA[ktMÞ ;_)UtwB`gC2IkzٞTY#,+?7n]?d I&a;Y; Au=ޓZj*op Vlmrxu5 BעER?CߺIk_[ƭw ɽ}C1"S~!R+mwӫ<]nR! \tasr$tHC:-BUegǧ$>ժT92&"x+πHÜ7' ƽ-S-"" ]M'VCO5gJglax% 00c:H%Ga v8O`K{.rU[oFG &]ZYCS\Uz`+*c *m\Zvژ6R&VXj͌P_M,*݋KC˕ nJl[J=n]|Rv!HO tym:2M-WVQ ǒ0 s>qeEMs ,qtKrߴ~")5='X)%Y..ō+MIk_ s]4+]?oX\~O&bğĩPrjy蟊|]V m^-hćW<Ƿ$эe*U]*ŽMR_ˑc z~ƀfnN03G$7rx~/ endstream endobj 36 0 obj << /Length 1768 /Filter /FlateDecode >> stream xY[o6~QK6M P^%*QD5~/PQsw.lck+[Y:ZBVcvd2|_*fs1ng.=r=VdGSܱ-?$)*Dk Wg];M2Si}*%a+l&%vIT.6v 9q'0u>1k a(8mۼ(hͪ&ns8+);M!|8be뒜II74. ͢½,oIk=x=ܩܤ0nҼ,Bl6 ) F.(mȏBˍeBnh~MFux:0۪hJ <TqaUl{5!=Dg0F-JiBJQ&%31+j9iȘrۜig<@m[BI^q(!\*Ō [o[±x ه,XuSm)l ru|(80iU6YN(ss !+hۭH t2\=V< 54G`K܁ loفjx -t\eVJ-{VP/oa09Yx,'5 Ko]&ߩ<'] ɡD,j M}ŧ4$,V&v #yH.~B;xĈET9OXSxA59Un|v*&xXa%屄+swN\˅m. 2>s{ 2 Iw|ȭ2 Sl|R|īkVk϶qo` my@Zh3_ [:)7h3=8.ۗp^fTi(,$;[=\7dW,8^}8}}3hjMG(^B!/ps QgԢn\ûdX &?8j- '(aO=sdGBcXX>9ڢ%S endstream endobj 48 0 obj << /Length 2706 /Filter /FlateDecode >> stream xZKW д/)K286+Xpg9XR~}ь$!9UU_=z*^}(ӫG__&n%ȬUUVivK,6*NmU˦__^d&w۴(m{=UXuo?~REgO"V@QH@0&]]퀀]meQ4 Dn釷eO<>L%DiuqXmp=Ԟ`qKw37k"&~4*Z!9RD>.Ӻp+mZ}sm0ot *Bv1DopwOU]GCmۡl=b߁̌UѷU_uޗ_xAɨzl8񘌏"22 ''KƱLAEy @߰.wmـA}T4.SPOyW;0 Xf]fnup!y` 0TwwUtrG!A\^85U}xQe~ɹ_ JQ sY^8o%EWppd$7ln-eӇv8[g3'tF9s 5.;{1Ҍ(liI2'"p:J](;}gFe"]lT3U>WSԡX3} H| q+XAW_l_ºs8$¦|8:tYP 82K"a놰Q53bX+*9RL^O<j_ѱaS^`%]{O]oα3 $osE['TV' w DٮoP8jpSl&t j2\Ȅ>tÆ<9c/$M O2bydE/Qzrkw0\ˠ<9&H;?_s`Ղ@, R&2͢Lf|2)8+`UhtctXFWj}{x߆^ko84Y/e{aBN3H }|wB4 'w6>A+m4ES/u4a3Vlx.߳>Ar8Sʈ$I&%媹|40xQ=!8Um`'ei6a; 6\9pX0 Aa"0_qZF"X=b2~W}⧿t'SW@?%4796CF4'u-.og6SDPAn)뀭R,sv)z^F]HBQc؉4V;0Am OښF!>VDym#r2HtfLO-̺>NJL+[=yt'=x1A)A N`['u< RpR$1dgл3(0QךA57iGV`4d9=j!Bk-aRLI ]166ɸ4,2:;X4ΏHH7)9*/: -:E5 h9lXtA6؂bcpc4C'GOhhf[GFR ؃wat,^piC\Kov:?c?f%f)H$lC7aHU <y} <1LC ~ J>ۍv}c/af\O0EU?@zjBjb}6~'y=;%%V m626޳['7g.,(V6p0OqբdՒOEoPąqAt˜>z6zFf1+l]Uꕟd6;ѪfajkV;/>Y*O©@ SH Q9~0p^\xs_\xo_P 7Կazjѣs .OPn f"uaחPu\ŗZ.I+~R̙>?=c .7\eJFr^ϥLY> stream xZoF_!8@,i88 b@l^)R%_KqiE{bovhv;g/pե{wc9-3)Obk _mq[6źZ8a'r9E yme{2iUW;Z=S >KB+:Bg$BVӴ+.*n:C*(otEȹֹ-ޒ7>EUu/k uYTyʚ]ݯ]ݔt`&\0hEwdޡ|؋]T賅 JXϻ2o^ SBxOf _K}^v[7/{5B;6/ W&^ 갶}e.d2^Lû8dյݷ;&6>\˼0:ض%(0:כvh:r{h&<ףKR 2hvvļvf?3~:Ѽ9Z_[S*6h[ex>ȵ8G5@$'=ʱ)bÀBsJ!=) *Oٳh~Ǔql&aJ}%HH4 ]S KtHİ~; ~jw)FƏ81HcKPxJF7er `Lw̓pVe:!(苺hT_xzpԶ{ N(9c BAAuKGX CPyۃ!@v.I#aaG wi}[ bhp[բؓtSp2µp_f ebx8XyAΝmΏ t0¥_68N]|т6w@#Y9>tH{l&8%4GS@ ɶn?ݕ|mUa{ZԻL;{+y%H[p K@R a%RGM7K&31J p Y[25!W+iw~I7θJ|ʀz9S6wTdפbz~+~Q_:Ɍw?e5lfxN9`'I/ 4'nŻVW˗o^_/1{Vo) l&m5SyS" ,6ӅKqfft[;\M/D?N_ۋ/u6~X3 7((Q&*Q&(% %g<*I="uX)< ~AsNf{,MAf+rnʬ͞$7.޽\(I_z Psbx^h;3HĎBw^h#YDtS?~*mQU) Co^zvҮ1V} z`C8~xmxfIxM bCƆ Ⱥ+m3< $Q^L,RTżyICADI:veHk`B'Kޤ5wŦ0"PL~XTqV{!Xu$k[B^3%f(rk 4W_!psb¼ʸ3zDnvr+' X8U:~Gl|f+ϿW[Hf.qݤbi1>̳j^WN=W\^?<{{L? / 9bA&?6;HTdaOJlajZ~KUTQ>z6A[ Rؖ7Tk 8AY,$ fkr 4g*0&}WzBј.]]Ͻ}&yHoHT  ,kt;5φ8 s@"wӃ(RCBnJ~>{n|VhF$W4{1?<1#=zDfJgImw$>w}OF$lv-6xO)(*vU7uR2=ldl Y{z"1Qs_xdHx !af0fxԾ39&R{QB@A.q*~ʢd$QY% aBmbIE2Am-psˮ-GoМ)2] YȜ-! ')ښ)3+ 榗ýq3}r endstream endobj 63 0 obj << /Length 2244 /Filter /FlateDecode >> stream xkoF{~C |$vS\Ÿ/AȵĆ73Klٲ/Wiw;:Y\rǫWo/\;^Y[Nl;_>;BfJr%ʫZjno%EI`/j 5vmߺͻf #J}I/]Z\kK+/75̄V}Mo8 <*Zgqo#QAu7 ,*MFİC~9|L}*_{RZWAl3`HK2"rX 0 CX(Ai}v|ũnV ٥-,)2':*hCJz[LVj;x{cSt@Ŏ\@˦Y5XjT2\m E㤪-{n0\;pE <ۇEZuZ^[uQK06I/wimUC}ⷄ H`n}G^z{(G}k8i͛80 %Ȗ?ՌFV +l_Ƥ.ʱċK(Q4?EX)e=zWR4)HOӺAuxsK]^վΫ6ޙEQeP-%@ůV2%.f'NMDBHt {fe)\v:N#1SFҖ?EFs +3~m_* :S#` KE%7:ʪWl@R.`|rLaNg(jntag#ȡ4ɓyE@ؕ`,pt?h- #uUu*H(d[`2vIRt2aK Rvffjȼ*ƁA5$_4B0cEUэ 5mސ8{xPAFve4>[P,,i"bІZ+}y320\g ]עXZIB;L L E@h[|1TVGRL2%gg̺ɷ&3> %]C7YTxY޾]ڤC:/?P~Isw}Wߝ[z'0`x/wʀh.o6v.+|C?:[I4zl:k8_(\ɳ "SE~d%HY%"Ó?~89e _Ny}~K䶯vΑ/Lq!.~VΦQɗ\:%mnf&uP}7ijʄ!ci/<n@2MwOI-0O-dÎ'buzq$X>2qmur5/÷P67֍?`\CwB8|z(4IrL-QתT|#9 {ygy=pWOЕɉ;"Y7S7C;f+idS9Iwq,JhO4kS{Epc~7ٸpi(Q]sFOh0!Kf)x- fA.lIaʺD?R* M qo"gX7ߴ=||i;$hJm`(1l AA2xa|qG(uǂ ;uӒt e^N|v> stream xYmo6__d7I,fh Ym%^wǣlKqb,D9>;*h1 FOx=@ڗ|j7qfDqM֢* 5F6솞u4m=s$u TьOVh`I㤧 q`'ϴ _%Y-F} TPLt=s"BZ*I n^ :h=]W.P>؏$N:Lb<=c L:I^p˂^Ejz9L3-Y5$d&_"A4  ƥqHz`n~ =Y(X !qm [bsj^Vk@O4MPgl-KI^҅F`;,@ldBH2|<¼g(^RdԨLV LH%Mϫdm͛% ^h^VCwj@$øV>D;"~,0IO:BO0|H׌Wq?<ӛg?v]rsjѮ [jW AJȠa+tsg>MWTn"$媚fZelw/r& J$+ⱆ e{Йa^ly%u>hll#9ԒϨty8#i[S>Q,(oO'n2m>9PMНBs$#X&XQ($GM̙ذ4节,P(3oLӘS4iU)p5p5,s(R)Ї&hz7ORf_G0q9 @]}q9:|$TL03@fn ! 'z9~w#i[G@& #HZou^*P0Aހ55%!U Hܻ2 6؊^ کw6=%cn(Do^ݮU"^"oՀ? k$ Cem} (;{rsPzuϫAj?n +^,*c[m_@7ƬfPIY R/?k!.typVy^Yt$FH._Bd-PTZr ]0Ew^*rۙM-^0ϡf)#D.Bz;> E)_ ޜOl7 endstream endobj 83 0 obj << /Length 444 /Filter /FlateDecode >> stream xSN@zlwK5zR$A6$"C.,ik<eٙ7GtgM$"!'ؙR֧U`>sL n`ST x6=YdMA(ShZ@R:9\jD.6eZ4w®dVv)qj#kdXZ̻Mx=|}0zplfXl`Abh:+(zk@7ɺJKvyT'eGM\TXfhXZ2d!`iT!s(ƙ\X QRx&m(ְ C?wB=gZ-=j.ÁK 75=YADŽFu:PζK&  ;1 4;G=c| endstream endobj 71 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Baseline-Vignette_files/figure-latex/unnamed-chunk-12-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 86 0 R /BBox [0 0 540 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 87 0 R/F6 88 0 R>> /ExtGState << /GS1 89 0 R /GS2 90 0 R /GS257 91 0 R >>/ColorSpace << /sRGB 92 0 R >>>> /Length 992 /Filter /FlateDecode >> stream xWKo6WآSڈ hvi0b;>FYbuQ8Gr>q΀y0ߗ8J4;}iWכ7yx_SDSbWl%%G$A#-p<03~AG&r8Crfo"dOlfBf{`Ea>Zqa96C:bHQbHOG@yK0;><(8$ cHOGݹk>6LG[f~UC֧E8!hS=9뀙5iYCʊ̰b8tꇟkTJ*l⺐rV)h*&"ش&@ɸZYů4L4AQ(ka#~PoЩ OnWfDžr ً3w Tmvy2Ů Q *F6@_Hup(jACV1-^Ll?JOjՇstq:ROǟax=sK^y)'Ü*<))||^;zkVepsƐQx#Z/&9KRhoo_o!AXcZ'”Ux2g.x`PݹΘG[> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 79 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Baseline-Vignette_files/figure-latex/unnamed-chunk-12-2.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 95 0 R /BBox [0 0 540 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 96 0 R/F6 97 0 R>> /ExtGState << /GS1 98 0 R /GS2 99 0 R /GS257 100 0 R >>/ColorSpace << /sRGB 101 0 R >>>> /Length 895 /Filter /FlateDecode >> stream xVn1 SBMN2I`H+q@-U]$$3v̊r:_p{wzauۻMЮw۟}anݧep{~tN?|nupV=fd`|dc9"]2h;]7=o98<c7)({=:\<ܕ*FkB$a$LY4^(UjP71R*]wQq~l;]ůX`phRXk ByI?G< d00VJmg×fGLt9_YwotF>wKY^N'q%-Ltdw( oaKwkYZ%rГDIy(;Fx_=|#;ڹT\>Lie ~MAOЮ+b\e:Jm2m/o{Y'Z 8qOӔ1hzHb.krA](va=M_ [a`Ó޸@*8]$^He~<*sق[A5.$54Hv'iW$S1+/0Yh r!>Cɉ__KkPǥ\oCSZwD \sc1#9[\2^ch1C6Te8#_z,127-r♃ :~~$Bڈ+% ڍʶt$Uh;S*].i[Ƣ& 1x~E'~*ST,1p\qw_Ji endstream endobj 103 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 107 0 obj << /Length 741 /Filter /FlateDecode >> stream xڥVn0+BEr%u!M^"`%Ւq h7C$$I$,yF4[60xǚvP`b5;[,g\m'&*h[Ԫ*P.OgK(&4]h({yY{ؔ@¦@`Y,zt۵{SexOv3]݄r6Ü%HMÈghuoIly<Ƃ^Z3 GSp 7CRyHQpzQ3ht]]=xX`SX FTw b: MZ6mk^E_؍yٌӼn#Y0j cD(o[£B5r1T@هHVtΚ-C¥v^}ߚr383i@0 |dAt깹2g) JAQz1Agm`βsg w?EfrӮ쥣8|}$SxUv@r, ҩRn vUqNFL6xnL%}岪NQ Bi 4,m^CV,%oTռ좘MV;m>zźsY>?z YBS2# ysڮ>\ooJN"~ /{P->*c0MA:  endstream endobj 80 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Baseline-Vignette_files/figure-latex/unnamed-chunk-12-3.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 110 0 R /BBox [0 0 540 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 111 0 R/F6 112 0 R>> /ExtGState << /GS1 113 0 R /GS2 114 0 R /GS257 115 0 R >>/ColorSpace << /sRGB 116 0 R >>>> /Length 1229 /Filter /FlateDecode >> stream xn\E NW}uH 4V&ߧ~yC\U5^+P'/ި_VȻW6O(Pa ?|3}>]M@WiOCRoj cPI>X |PYχxcxSX$uTvJF.ф/ Y˹x%@).EA[u1h4#u (BP!4tz4Lmf! :!]mWz~.s`,`j&b)JyʩQ8:0XCL_ޢt\X hrh[VUlVjFf_%헮cP]08X AV[ r רӱ Ht w]D[ ە<bkD `28q:5 m@(Ёq. ܂)E%Q 7*f)䗷;PFma^d[;x'bcxzw*+L;+3 At ~!-Ȏ,lIUbb|;e X|^IU\8j삨D0L^\xׂC6o1QM_i.nsuܡ|O̼ˈQ,cc_{fvTtj|ߖʴ$ip8_Ft*GwNFʺc˾? [8? ޭĮEya|MINÕ Unmb Wi3E}>?5s}lx1 E Lo 4-w"WprBpV|$}k+I.N/|{Yۇ7 lGťآ9u>x1µXehmVb|_\ۃN ꇹ1UBs㊆k5*fWr GZ!^wN|uMV -)MC-pJoߴ@N 9m ִ+]%pJ`J -hJ> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 104 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/Baseline-Vignette_files/figure-latex/unnamed-chunk-13-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 119 0 R /BBox [0 0 540 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 120 0 R/F6 121 0 R>> /ExtGState << >>/ColorSpace << /sRGB 122 0 R >>>> /Length 3592 /Filter /FlateDecode >> stream x[GϯG0}~M@$x@(XIJ#"=UͶ}|StζgLMWG8: %?w?_x.^|dnэ͏o;~ߏ._߳ O}w|8[8rgGao#5:C#\3 gHֈFvxZ8wy)L3zp9Y&هlde ~E85Wi2I+M=#0Bj3J%P%х4ei5_͢3w3'vhɏ 4o# mhG Ӂůˌu;GK=/2޸#4 R ] O_l탍TE0W~e~'_4 >'ov) ]GTCQ="RTOH=u9/G/b:%_2$~U/r,F"lGYmEOHW,B"a;Xbdsev`$dgE+,ҦVT<p,_a Qƿ\O"cecXb,"s,vXDu޲c},",,r:tƅ++6T3jRdH\!<F@t&:Mxď:jpQ؈фЈFD Q<:h\x$ .bmE#젢E &"% HDJ"<DtxClOy#& ѱ䛂B\|vBMDp/A``%\0q0PQ&%rbKЬ&'' U`,#u(H!44|@nR5NPO_@j$*l<$q^OjfOj6IM71$H vXJ3:,,#I TI'5an%XPa)t .KqXy8&9%7&RdQsT ژj$d@i}WLiԽ:́Y8cRhd(˘199 2&7G1d(, 19J0&feIIy3bo\LpIm3в$Ť ɲ`196s+&f`ŤɾR17s)&f(L9 2qɛpbzsM՛T$Ĥ0Q"1ټ9#KGL&oˆɶY$bR@d9q3bRd 9e3arl{d|5ۆGr/梷!sm Os$V6-#s;ơ?sghoT^q~/eZ{K!? /k'ݑ8J<}/⛬LA~F;|n rD~6Əc|]e: gGxa9-}{/"'\譹qeoM. ɫkG:'M QEA83> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 127 0 obj << /Length 1380 /Filter /FlateDecode >> stream xXn6}W ،(Qtih Zlue%/EDZ4I /8$gI;r#G&Gǟja"wIj:r&uaL&+fñ6SGxNVcXWNxXX(EdOSXEvZVC7oU. };t]CDRhɜI&5*_ fyk5iL5kQc$` ŋg=q A"RqttdLhu)X9n^B .Hkv.%",bxz4ا_jFH,y9tCZ"~˜ >Š\{e6oj9kۉ6SI'?W#HV@dQ?H#PDgEݯŮPb7DxSŬ*%OR.4@6盡̊eۨOm0=Y5sY# *25)MX1VBL 3Y("VmgbWm1jAb.ޗ}Ø:nl*VbRFmVD:Wkd>uUnXs-^_tp57q&"SM̫A&T%9 Wr:!F]*0-S8Xs 쪔s*>.vQF.pxAYt*@I3gڷ1G 3 jbVv䆆 {ݡA@O6 +q3#y< <Y8p)5[c0DǾ'QYd؎Sk- *߲Y" B$2F)zD@i&49H{w78p{'dkmU|&P·@by]5z9Ohӟ_`h8f0͊r#uyוTʗIZ?YDOW3t}*`t&_%5널eIjc|E1}7til%\_$JC"P^H[ߍ7#:cH+8_,b1RB[/DjԔL/.`z6\^>@FH^9ZO[bչ%DUIf^ͳ^F ڝx|xn]y镜EK?+=k;MSW^O{V7~ sϣ9~y ?w mPq5N-]Aϣ ;0 IoӶ,v뉏(E8!SD/} [Jz>=7@'5w <ͼzy67@%Y“hxY kOA]e^gRU-vekx!^<Ϳzؽ\UR1Ry#D+Fd,89xj^ endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 803 /Length 1639 /Filter /FlateDecode >> stream xYnF+fw1;"qbm@^DW{.Ēl#HӍ䜹sg )#pᅉFp: 7" A$Y$_$+F[]00VX-qZX6Daz'lD Gx)\,,3 dLS' 9OQx)>%׹[ʂ ڜp\sxHp8GPBtrvDl#&LA 1)0&L (B" 6E sJo ?9f{Z#Akd &Ho~c6XRO>y3VnV}^CwbvsЊ%lRGlW f;aL\ Bj lnnh#,]a1FkPXaQ=("؃ A& {y$Qо.dk}&\w5.~cג)ԛi38PU|+*ª ;5*?mZ! ">SzaֽO2 sV}0 kF҃I@Zf-? 3>|+?|Z㇏c +@{aHLos%_4e6Smrwͮk PlB3N,C{a ʸT/e\.-5YVF]qݰB  I !B|vvoaJ#WilFwdR/lMW`>kw^X$lkesilƶҝu6[W 7J>m% 9[(kpmqݎVLvh8TSIs3WÏvC.)o,Ү^UU[B a= m`1,Z<4>kFˣ_\/ZD wnsP^wý=q(rǑEM>} J^Ov'{>?PA.5I|&rPϛU-7SEju1-?VJ `>>UWGJi ȡ53% 6s5_O.|<*j.Ie9NCOW v? i3A`ߊ4gr86lYIEwяzN5=^vKEX+?a}_IF6y!%;g7 +1Ḇ sdC&b&?w$ endstream endobj 160 0 obj << /Length1 2079 /Length2 24905 /Length3 0 /Length 26138 /Filter /FlateDecode >> stream xڴeT5;w R( .Žwwww/V)b}{Ϲw<_ߑK^IFBI(lfo02UA,"f6&JJQ'=Hv(':XXx(@i0 }vhJ.&n }Owdeag vF?+aػ;XAf&y& @c-m5@]U\E JqUUSd + IuU?j@; ڻO?jjJ p:9[)?ܨޙjdoW3; _,N6g'-Ƹb  ge 9$Iiw7Ϛ 8U\9%%92:߁f":9!oz\=ћlS{V̭l;3+_6yai qU5Fw߻brp+zbr|.+/] 3Q{;w'f'{j;V 3?]7su`VY9/݄`@SK?R3{ |ƶ@+s +7]եAߙO!}?f 39˻h9cSKVH (c;+?!?TiVV@3%+Su_viw ,l;I9}б3\{ע  WEt%G.Lͬ@6N.gw qrXߥlK$f&{ `ng#̢LB<f n߈,7bg7z8j#N{u{u/?彄?;? okn;#wJN}-!;?;wu:u0?;+\;ҟh`[SVuqjZ#DC\o~_(Ib0rXXrM5)73\@auޔ?:9W`[PK&j5}W,w (TAUh/'ŧ*֢ Ʋ}lK5Sg+K".<!_MF{"]1N P?6:L{{k - *~L_Q«tF20}=:)jKf0Q5}v4 $u{*ibN"Ld.C3&BJ3m ,}aD5\ -~, DjlxyP 6! 9Q7T]E|)pM/+`aF:q, Sh Tz)E{caG|t Hz]|19&Rmľd,W }'q?Xw\*oMJ-f<^ K)Mtܫm {oO>)lb11%?%;V{ulRs:Jޛ&!땇1>K6}%̚pxZ[lU=nEԡRokk ^K ˆ{:@AMj L#Ʈ%(. Cjs,WkM8V(Au;01Sio* vXR9$xV= 58"J^_/f#Ņ)S0}j>#0`A OۿzƪBdsud./%^4zL#-#k!E'XwN5uhSF3UH|4|rUnr5xƊkG E6&F|BsZWpDu{G{S异"Γpɪ&b#^|\5,"-B9i#I**GJUR5dW`C]kw"dbV~0f.n:b6 8–Pa:6<6ԝo b5Q;ԗ7!xטb(׉xX9%A't `>c鬇wcn<"*ћAavF0""ˆٝ8~ZhB_2(:$p&zhB{9Ğ^N쟺FB4dO͋Eު^%>ptغԸ&^ӝ7iҜ8Z(ù_Z~b=#S\ޱR9Ṁs$5 sX&U0uƸjzRcR#..} *wwlŸzgV3DCk\l$~|]]uYͲ':ZbF!t&ٝRJ}٫=]L|V*2tnTv#Ds&p~AuUC~tcQ[= L坭b-Db6CI-+s_Ldg߆'BQJ]"ִѠn+ZȄ7o3t(]膉'ăah ->V%E좲>x9VX>ϏHxb6L0x:7ڙM|:I2Ijw|m\)g.IPou~Ykyyd~.׸Y/RšT[g3S?Y0ܜ= jmY~pB ?8[{ gu&uPd!ew\vG34HwKTm<à^04 =뾪>+>Zf}٬qJV|6SlۈZKFU@O4'pUJ4V \lNHoe0鹉fAJ zΧ]:9_č;s9ybDr҇)2P5k{>'޵5!G/䱞/[KZ fMLL#wF=L\S-@ ~}6 Aijĵ &9aφ s䪲^Y.ܸoqu.A 5alE[ b ѷ(-pAN \>,!28L]d:\Dlݯp餤ZI6*B>X>+Tqe60-*Bښ=;{Ido!V\3Q!3; zv׮hx1G_,"gA{wp%2w-h+wqE;&IYY푚/twV: m\ $6vWYYH@%tu2~— Kug"#V&% {Rڃ<65 dar=Yh|$Ü pM ?lHO4&&pM 0~ͫW6άGپ_ח%5RHklS D4AM$shiF6oȡDg10~&7㉴۲k%T^4.d6;E#LL$@&"MlZy*ZwMT~`,AE Y 0n'fRÇW[%ౕ}ݘr'Ojs-sRGPX%.mګ편/Q'(1vkGQsά9@f#(wOC$fo]M@09*^v@ fh|}Q x~ E5p(r)kB:<290Y"q2 z@>͡7KX+qRVc Ч?oX .]B=ݔf_RBK{IL8LE'{8Rv1̼ CU{0[ -#!Q[HZň+R/eoMDhvd]PUWdP #`s >~{r@%և{B>xSh S_gVjsJ *]Y Hkj86kY+?Eo&sd(0SP X&?Vkw5k *rL]0λ*^ k6U48d$ =!*\IdsϢd?-+B{*O::†6(}V4CY7Ur(Ux| A T,ׯ:l/@фҊ|*r((n։gh>'8A#1vr7U1ִllFXZC $Cj95fߵr]k&n<wCAcI V.G=7~WJ; '-ھI:֖v T@mi+Q+56ɛQn8A}pUtf=tẢT~=fX ;< .0x f\iu_`: ݴ>K;MmVv Fi_+*UM@(fI*\b3o8:e."N X;8;"):uNHY UMG>' Le(¢1v&E X.XL[a=JgT=S(|2FemիEhMt9+bJpjŐ_cт>'4- (?NkzV;GE MHT9Q8iL|-ؾqCe [n[͈`rH i<R8'AM7G#cXqd6OC>|09;]kv;M-\쿊Ǚme+Y)׌<G31'8փf"N<(?P$ܽO m;V5@#kp OTi`QLtOLկ~Ec!ymڏgϤgM+"W\y뺩Dcb tlR \:E|źGQ;]t0FV>,p5T!6̑hݕ;:ZbhaiԴL+1j,NA)3|{:\5CsWm`s*JOe"?˧zvD$ԑV>8J?^t*dqE4>wB9S91>d$Wuۊ6ֵႿw]l$תDfJ(3 R.j ^?!/쥆8Ji<'[GD؊ƒ7dAo|䇑?w;yzUmui;C|⨦*kzO{R82QIJASTqE՜#o?[ԌriEL­::8a2&( {2T"ԓ3(҃ƱڒIjRrIG?/!˅ Gv TS,-ߓl. >9ՉOB_g i0j״suD2֞SY ϗ>^x(\NHCؼdҫ~qU&v}9$N`sj﷉́)o">E8kx,ۄ4CT/ۅnZvᗆ HVD5`2oSr )~eiw8Нw%0>ƳFaZRgdh]%"Rmk:#Y>z0H/K*fLm(.QKR \3=Ze5_1ORxq  ĺC*X{A 'T?kp.}#dž+RiZ2*qxjIY)'YJ bbJf~WpuSfO]U]#xj-1@SޓgTx}s-n1 AR`b _kmiWoSu.+/CnU䉤"&V㳭!w`iU+0}.jJ*IҴby05dfڱ*yE1;ȐiT>kL|@NlS.2]~=uS=TړuWvo# 3j~$T-w2ͽ^[)ޅD_fv" 8 1pw՜3ocoYWQZw/ǿnepZU p/x/8L с+s7Hmvi.T΃8 ,Fw`YzĿVi^u23*+K벴Zħ+` y Ԅ+GZ]7>J1ȴK|liјg2GkVИIg]?.:9)(dθ!h ePw Og)vAmN>K8ovf` lb-ʉZKx̋UkiP# QrVklg(bZ2";Y#J" a2m7޵bT=UE8Ӧ# $%EHnYF-5eo}EI?lW,tBbReyt[ݣ&xޤW/$@?MfcA x;?cu)L mw-˚x#m uGP.>gG6 ]qJ ^ÑePns}U|Q`z ]7R5$Z"*21%1Cwy[i1/aW՗\:,k l9[(iWJ@׋8BSOh,C/`ΜN[(Y0v?.2݄'Q$gί$ep\KSŽeEC~7~yp>4A⦟!-=)I~*[YˆpIBQ͹t'HĎĭEܯ9}DKÃOHԾ˘4F8@bpٓ}1Fku 2kɸ/b4z`^Pe蘾à!=ߵl6E-v?|XxWxv{&Idsh &'ObfHEC,\Q܉ D0E>{{)Lb)^v5/ wg֖ fiU4,l:cqN'2Ab>U)Й݃?q ([™,ȍ,px(rֱvC:w`pբ FD%O/mapP.ˏc$ muKdmtC61^ Nj6OJlE=aI[ɠޗ~9|bqѼm:c2Ƌs2_("5iWu(r`S¦s<¼I I +EX׮\(NrKJ!-oጹlACX^,B,6V`A%^JǛ^N-(FTFJOG)Vo̢U1u0ס́]}uLRqd^EB-g Ӻ7`(ۉ~|Wӊ,bE9 $^˳Lgc BaЃ#)~hgLc.wkq<.>E3Dy#QR*h{'L30f-0CC gfiG.|+ _4cD;W9 FڐK>DE:˹VBLNX>O< ^aeGQ5RpK * -xk+]jyz4)Uo#<QEM|*Z>^2$mʓx|`Q1($#6+ݽOe% O߮CYfR`ѐao|>h_Vkh &dm%1E(]>ScѕV&dKD`i)1u:sU1Lګ7>kdP.S?ߵGYhbAS~t貅!ǂ˓r{&CaJ/ ܍ G^U[׭1?Ef{xs ?}d6k| Q翁0L$j#g'=},ٵڙa\.{4*?dg̷ ?cc0mI3Zpo*@.yl7VnU!?qwnW-fWa z<,3vc1Y$S|ϖ_;;];Œ AoJTI*Ś )BqpgGLQ((^;5-!N)4LiPUQ6<=8PՕJs9|fODfPD/z"JA>-ءŁZ?9V>6HIL/]qCQ.s!OOhOBJ/\GXhW\D;͇/b~D"mY$Q2)]D쪉{YL&A\ҷ,m;~BL[ЌAHƴzgr7WmE?[ͦM꿀Ş㝓T!UJK5M(} Rl*X3`]7`|s;["> Fc09MY^*Gp; q&odW90啨A]&#apVNaq v+HiMEޓI>&@Quځ _λtV_Rwqo= HKs{_V"Adct?wJr]ԩdTik5cc*a+%V_"ZGH:VƵ-{54է1N})$]?[ =ˆrj ,Jej,EqR4F4zn~X-;b&lYi<-C1=;L&2DmLYYkhMPpH&0:) C>Oi}-0fo6 G g1jydM2g4ʴ|ocq\jBvK)Q4=_bal^B!mQt[sI"3Uyo7{DZq XdD @s*ˮs5,!@:YI]٥d!WՊ}L=(j\A{g>l!әX³nD}9K ]@vre*-/w,2aY̐RmךAQX 4?O'VLğۥ{Iy3 &Ǖ;lƥl?BmGe,*l12Iy'Re |+_STP+ݕ 8c:DKWRS?L;I[[ܥGo4k]ؾMq5vm/4.F~HOP $e4Rf7 WnTѤ2vPԊu=T9'KKO EOd+z.ү?p=Ci2\7zw񳧀:䰤8JANz5ƶH 7}Yn5(.4>7-B#(6[jVaPk&|eϣխH.kvNzQnYs M[ڣL.n&G\*VNsW*e|3̧)눛jߺ>zni9onf=0u0ɢ'>P#<"w?ntaQ>F`ϊ+~nkFA"}Wjo) p4BJ4}n:t1ALFI^l"Ekt=3 6^*h%qz.nE淎U+ @X(ѬUy-h^Zyݩ7XAk0y$g CQKK=AO ,lˇٱhآ~{m0?motӆ.QU~;nDo Ezm^`ܫ9q-tYۢKΤ|#ZWeSFWAIKXd1ݶ\Nڊ]v5&F*<9)%Px@9"a;,{"JAj56"u_o9*Erڃ ;o9XbTpWN0U2Z9z;4o{,  6ۦd 6BoEzx\]((bD#ަVuKà^Y&&#;ס@Zq@l%憹'K_YGIYPSay* ]{jFpS)zx%#v:nX8J=kq7cK]jt(]a)c3[m(`sz~ƷӐ_[uJ |+)\2'5X?wUrDIlXr,Uc)!ڌa\y bAa<9[~|S(7Tk>\lΐwVQ8 BVqY[. =5`d2ߵF+UuנCyӪ1FԺxiϯۃqJpf| ,=7Z*`C=` ~ V5c=c 瑸DJD&s?$H\S-CzwFuT#dj_t5a&'7y搢`'>}J^pA0F¾aPhXzP:I>򂪬[&=wg$oZ+$\%G9,eU\y`05 y (NG8pah8wxMG K䰼C.L{1k/ł)k'ԫk,}ny`9|զuFX| [O弈TiFu"+.7iW¡!WZڟ&w49{8Kd ~{J6[$U$%fO#U [_J%$I%7eաQ.mPaqJ"-cmTuO*FK AgNeiZ 7! f  I{p:e>X^bĩ$ *#'H~ʷ+=96xa]| 2,0x[9g,TZR n%->)g8ǵWd#`Fp Aq񠲹j/J ,cȘ묅*H,.}8x(BE𠻈҃ k[%Cf(i&-,7ߔ}+a-?҂6ŧ1aT{֖1=Bșq_mpRQj?Sp-*֩ML^Zy8~*}-: u+x$ߛh8=TVn=Q52Vn\++wO3xQMIm퓏=~FIULj,Ꝏйrxk?2Ww_YrV(o-;"uwZUeP3܉M\U/K{;ʵcNIDg7o1ԕe1S`YCAxSA&i5d2Jc=XWC iqls(\r`mcswdk /|y|7({@R!ίfB8yU|>QW.Rjڊ"Kz O=d;bviɜe1meT<%/MeAQoy {jvDm )ë~l.赡ΏA n4ϑPy,"ᑻVfs͛F kS1-˜~D^~IĞ-̼e?=>tu<s9c~ }})HMp4'L7bnFa㬟h\?fe -nA+?x)Չ5Ypz@ {VaD2X4Q|ѐ?"5B&0g|sg^&GiWHUǣ+DR&8FWͼ!a޺Sf[yKAnN 6*yimVq2ڜV,:p0FTPuI> @as_ykIO.sAž.3b)YiYh $Mݢ%Y<EYXAc)xLe]V ҆{XR\+GV0(A@C$%Ҁ,zvWԘݵZeڤz~x3чL5xGvmbT㑜*7GÂmky! Sr+ib$47:u;B|2R^_bQm\UHb9c86VSm!,DCC}{˩SD*3C؂P⸧O'gLa# 5ǷB6|%A[Mر% :2me)/Y"j=a= )*._#)> Dj1 o{FlUV}K))zP2}m&NC=SqS*43&Z낫Dg!&G8;Nɷ~r l~%a ]$g!ܘdZYQj>"RL6e *)& k'y?a)9_( {Ԫ}?:Haf*۔Rɿr`tj]AehFϛ?8PUƂOa|&Eޒg _ %i|Sѡ'Rs=m.#(5q=uN[/K.@(`2+9ͮ:h_Ԗ#Yz{*iz6bBʍACjYuGibzk65`5ɛViHI#Jز@dPr'dp/ p ~hG&"~7 ,Z)I)pmy ۊ D왩YF`2ا!*^ώa/ڌF;\%ԸP0]fD+%o]_u-ָz{8|S4†TfG 2%y2-#]5V@`2QVgs)vzv`ĚdD{FJ 0͆N;my˾k~HnEG/"⁛R~ԹwoXpRCǣx YꏏBh=qQw6pH?z#AW( b0(Y.0S-aدK!ƞoUj<N"9\tQGOsFIy*ڒd+ Ə OCϞM5" /sS]?!(zxȑDơu ޽J( Hx_0Yb#Ƨ-1qS< UapNW~^^vXNճ j_[6P8]ze sr-ҔL5ak/jhyP'cs|ԔSPm6Z.p7} 0uR*H~>ouNʹKj? b- g ڋUvVz" *=d ̀! xI6ALvyzx^ KJc pIޛ~$A xQ%YK23tk3CBf|mL35&gNF.u\`C$FI/&FjVʢ.(,&9V \a͖ΜsxsN&_O?LZm6zYM\E}HQeo&WyԵ10r&4卸 rPFyn VT]$b&8֐{7`v%z@ek'ݝ^U<ۑC= 8e6Ħ5=DЏ(13` 0 # Ŗ}U EH5RUhkyEԮ ]yjV^!Y @^Yp 9duk߰^¥ @{1#K 'Cnhy$<*;]Q܃4͕yj#kx.GloQB(3:%aSgE$}a| >R=~D;c{IR+W+en/?o&ɜ4 [ip6wvtϖ8O9I31rGEx'Q= ;U$kISVRۏjuw83+Jm~fAW"HRٓ?D. ޚib6΁.6ӑ{-dขIJ]VQ6 ' Fbu.PZ.֯g 6 vc׬_x~Y59(M#INYgim;h}g#tְpQ`i/<\]1cR*_CBF>_͐Օs3xgrj>.B=6\p~2>SfRM%7uG`4)q2}:$ruB:]թ}Od{afk>t-+e &٧Wn@M5$dwI8 X VBا@D W*6s:׌%װlb|G![;e{:ajc x90~f428[\&G8>o覞ݼKZcR`I6.ǺS&St ʁo:SPCg;%wE9p!UQ=АWe&[q xy|34C`3^=+߸1-|Cp3p:2ˍI{?cZޒJp= -~t4C"˝>_.>v۞<(xO8{5ۦG;k9gÏ"a[L59UЖjS(%wV[Yoqv_}ҋKhZ*@BJz=\ HPG* )g2OQ?}'7fxHVUFuw"9y>Otw$ۮYm)WENgO !{כ=f'" l_[CP$|$=` BReJL0 C>*P5JF|V})qK<4;!b}rEdOl>c9>,$ÏF,ha4ؿׯ\嬅HL&ds7"f63ߗw#je4pv\pumu5C KJfn:R*|XF'w^"IOOf(:O*}tK:7?Zyjt&wm!2elSZ eOEtK-;9x0 Vjmc5@ @]7n`;l{qfMLaʮM60P)7@ތa@zS7?FxH'f91n"=_\v5STQkwxt2X\RYf'-vH,V9"y,X/Aɲ¥zQ)DįmoƸfx=Xbc:( C.XUudɫx#.Zi&TDO2MG긏†=rޠRn[2f`!Lޏ̒W#5b1,NC&AqJV4Z&q| 7jˍ8 ߼f Լ\N2.~O{ bvY˻í Θ$W?BcRV[DLd w8[߿=Ԕ R'KCVhx $B$ [C;K4&bO߂X뼟s| F޵I\T[١ZxcB4v*RbTu&Ϡn yFs!FbCImemR0dž[ay˓}qdbY0JTF8ٻ[}S%#RjmrBR+>RծvoohO4J-ώS_꭭'݌}2hk\y!IT ~o)??{ą.ۜI=s!ooQ¿ذ7%ۏs[Nx[dO{̜>Ӊm&.^B*=oS4. 9u.'-ͷ]2O_zr[Uȴ?hȣu~r tA Q=֢)>6ѥ^C>B# }Q'TI:δ?[L/0}yVr!21gA"Zh`$*]i{DwC4H #\/m_Q7Ǣo4 ~*b츔Go(hڎ'݈\28Cg(wMZt')د-~CUuֹZq/}yaņj3UK]_jLTʆY]"k$x"Z$4$OwJS\?4 dp>0L&Z6zC+Z"9PM@Mf=ORu )|ʠP`_!&ѷ>Ssrf !.}K)]W,%Y5B+@ap?DhpQ_co"!J \#Tr mfyjPÀ`v`j£MkȁD~ݪ(w_m6_+-'EWhNߒ| \aT8-\T*t:|4Ё=rݟ u>1ACSn1,`Es]O\4>q$sn&.߶ǀYe8jI,ə;df).l_ΊO|R ;b|؃j2J?r)Pdf D?GS?>[8Wk-$[L]@\asgj9呝VMh*=ezVW|JEf!٫_Hn~zک˻mmLCu tFi5ihҢe:U[P:Tq\w`]DĢTAU7߶Ra_"uԶz\$ 5GN<g>K4v_zI p#dέ7M%aSYf !1/ 2x(b#ch$2om*e-/"ۥ 욥UjX`=tNJg#8 @mDA9dɊi̽/ }hBC[=lAG8<'&LxCY ͽ{id-XZNШ)۰5i,USp./w+8$f*IeM"jb:X1Ntm>|cSPVIbAlCK32BJq[!`e-i_z"B#4A$a :O Qjn4 i{aGّes,~m\s#yxV?S|/^Rk0ɒ_RI%q?H/aoDf{ *ރr m;g+j,O}LF4wM^%<U_`jrS-宮 ŗg&}Ul2^4cK"BGH־ .g/Uc8kJc<2}g>7 mi yh@ E%00VlTu"df5"A;It<$"ؙ*4/Կ_FHs)Iqˆ7f2P=-]%8y/C{^zfW1 Yzحr\~jrry-ژx?㵫Y۾AjīpXC cj`>]ƕ7y+FP}d?:YtFc\V7V\C36앱 ʬ}E2Lc% iBX7Ƞe9D1?:t^!?QO2܁%UȾ%X lY l'Ήo9P [xh^ʿ<5XZ`4tZ͊>Dc;nIJzH f~!IQs -:Gd,af`DgyR= >߶jyX k*>ܓ2!r)tW΁tfα@sO|}PO#R530p…;[RY671]19 HIxNdaq 0ޣܳ$ըcleߪ\ޘj1([j4bBX9(tJLm[>pVg,"gtּ''"YMAIFT]:WL]yL'7A || _e۾JьKi `,|ZOLdL Q6Ԛo]k4^ dN]3?2WlHRyI QfUzh_-@$p? )5NP#+u YYٗ: ۠ו:+h☍QF_Įv$\#U=̂U}.HovysOQ:39"Z&`hZhJe\9} I !8 }V|S=\l(䍐0G::JJ0P.Тad. .&qT}Rh4XGyoWl hS!LLK,V*kT!dmR:6 nڌVhEiTp!>\u aGD@Nz?NU qv@ӻ0O󂺏|wܨ9 U D8:*] _L Z4[ZϾ_1۔^I|O#!3נ!š.>.!/g-'22"lkynJq@W)NDD [(A[2=hQ۞mnhT".HՈBB@XK*0+* W,ZsRRog>ᷡWJ ^m^]ߏ6h-_x_"@OU"e4>~ePFlaa x{"<./Ka7W܋S^ D0'Z;nx%ay Ȃ~?{tfއ<-Wƀ4ٻ)aw+k2׀VD*4._WO24~3$-΁xo3PF a\s%%'7T0gol=W*-)bU݈`I~ſ`?Ʋ?t3֥vTp6r(:zu:wD[bklRzֱXSf6+qWԏaQ* ]?3 A;fƚ! x[ wb N>zLY.W.NHLJsVsN;١`؉ Bݾt1dx#632}_J)*Ř3bO#U_cz Ԁ(lٸ c0δr]|EojxT6y3Z;R:.Ծ8bW3VمH`b FЪiqƌc>7'j\ ;۩pֲ夌 ~N`!RN5$B} {5%^!8cUU@+%[>7pR|{+>g1Q@r2o4r`kՂ~ܼRJ`w5C|D[3ǍE)YdM0'k#j`ppTk_ ē" śK,T !+"ҙB*2͖zjx<<⚩)kFP>ce!9 } _бOBr%rNaRT/f4@=ܭ |{zg6?`ekyAke]գ˜ϔvW" N#NK9n҇tGPD| )L1BtAl^VٗGU 8GsmSBA/(+ # hki& 9MI|{̲9hlcҳ踵w`BqKr!N#7+.|[ a?Épo'5JJ,,m>^ Rr N ,R$ɴr++V(4*{{a)qaDT5H>OW[Yd=5OV:D'$ q- r>4SGK1M$vqm3iѲ{Uk7TgDmctݹcӀ0N=Za9oy%E[ bX4 UMDQO,2{${QJĖ,]=Y3M`>b)p]6 .6߽L6{ϵV-˞[?'1ufBMI"7ok9=ѳCIk*lMA w:SBh㵪Sٷ'c!?B2u`w%U L=a4 ۫~~&YxSQ_|lInK;|E| Šo+'K1pWc+ޓ!$W׃uӉr6d/]D$h&س34fuS]$ Sd\n`5Gu*mp~PO sLgOeQA:O >,%.,?Ŝ v;K<~-^, endstream endobj 162 0 obj << /Length1 2036 /Length2 23317 /Length3 0 /Length 24523 /Filter /FlateDecode >> stream xڴzuT[]6ŚRݝݵ+)-R\;}73̚<[>ޕj " S$ 7q`aeٙXّ()@k bP2sysXy(R@ Mi0(]L=,2مM tvҾ=֖V.b10[ kbf rw8d 750ZY@u6@CMBU FX?.bjR"GOuˏE7DT~5}}eLg-F Z )y _]tmjdqP^:;d?6("xU~.ch.lH8="zBPYG? ;&Qr]mWKAļkJHP(&x[vĐt4#ӵ|Syu8i'wQl8s͊K)^Fb|;-**1* Bl=6K'|Hġ)?,#_8o #zo"|(J&pNJѹ RE<䷲vHM "_ 睡xoc^9WhՈVN*4  <*NE*4fz5D܊kN{b.hp'S{0҈=ߵXB=M_9۱QTXx6 nl4SqC&2\Z3ZtS8˓6FZl/sQr5+mCH\dJ;z}f)"N8G ЈcepmMW`!4mNaQ6'y`P+ϑgae^,dOCN~̆^ ΍siA 8SsDv"X5rE6M 8qikUm46atAleOݘsø{7G[8Ҡhk!W&Y:CM\>DG GB >FXáEՀtTj\U"x,\A.1}\;430S9dͲ#W/ eZF#}3ƙkV/tER sTU2PYz1XiT?&b)bdus||՜ FA}|X7*Bt cЛ2R#xQ,n@shgG;l&``xvΗ'0ѿ8R=uJ2-V0rBv+8_ !-KI&pi? yrYBA_nߣroqc^?K#V1?d%@gɇ;X__,1~zqH4fnbіV \ڕ`s_#u=<Ztwۦw=;xB,Ay}0eq8|fo3)qB25: Х=-0zpE|/z?!~ekl4?5> '9`9l*X,$#gBYVp7}jctU]u"+ 3/ɇ_'yݞCǽ4hY܆TQ@nNrn[W@p)FXUc^j<8#J0K>d>"[5ԥc$V0ݳҾ8߃ƌq-}|Q۱wޕnu|<Дi&ؾ@愬ҵKK]6~nvhV ] q\($yo"1t|W0Dd8dj Qί;W/lɐ4ܵ*VT3q9oجKmCsE-EY[(օzgZξv@WY3)_S_v Ni'Mf6*[CN&8F;OZlaB;ͲLl6eyʥ~=(~ƻaܧRF|jJ#*x(z꿱rȤ`?%J0M$f֌-cz했 FDU]"kypGJڢ@}fqW n>a0٨|i?N!~n FL;T&1"|لp/ޣFmfT/+|mE6 )Z賒m=IUYѬK[W9hݜmlq샊gYBal1 XY[ U8iO2e&zXE9@uu% 3ȍ\Wt*V%qWB0aJ`Z}U6)n 8uC11K Yj[ D2]@Ũ')c%ǚݙG`kMmLWF@ W$č bv9KW͙S.I7٥FO-1K<7:ƒV?CRs6E$ޅf4n !X糚Z~R bVl&Ј&>]0ӀYzW%PL ), Hg& *#|nPa7OU\ǛmyK(?) Z !I+z6gPf5RrxjL]A:1aG!ù蚱٤:v*䭊B-st*1g6zҮˇNfb6,x3&-eT E ZM_ fΡJ]J'%l4"*eP퍿/hA}U„hvz##)f g"b͊cCwHْW>N&&QjHٻphj3l}y+O1?g꒐6(^H+@k|iAB/>HZcM:<+;T"7pء:Wf.^*08 wSw"i/O(J#yI6jQowq+n%K§Rx͡zCnGORl{qD- tR%iә/?,)^ge/AcɾNUxa`[[(yF,,O+㠟?H-SB  Ez Π~e@[Ҩ8aVa})Up3cI]ԗxh;Slf+xpȬ句Kk \vreZyИ)G0rd'J z=p!if7FUC .|U=Y,FeU5N >`_>lqV>d5W^s77'2n-FJ˺m.q>sJ{w/昩ѥXי@xǮ4'u 1Ij&XV|b(f? k:Q0y/2]ne)7ZX 﩯 -ss:PvnV A UaJ,_i!bTfƑݷ3_ۮTwP,1]s?bNb43|6K([kCO3OϘ3;XsH=J5X-^S37OGNx3Fk숚!yV(G៩T@6^sNfئe^}pm&ݗBVpw+W^QVf:jR' 1 ]52Ex'VvKsQ`SczPEZm(rMӭ LQgmÃg'ЕfR {Y Ne=vk F`u-@!i(4". & ;1;h{'PMLܼ2j/Y{ 1 b7rL4e5cnM=w]t}SdM&2TOG@eR!줱'K%ӋiNI/j|GnWO?|QBn4O)Yz(cByNtO(+K~k<ۋn/KO^?U1aTn(Fpe;dyhgl.2V&L,(0q*]Ϊ{Ѣ1 aC BD쌀Ls8&u=4YyOlܕˬ0YHچVF Ũ3ǣr= -4"鲣QcJ;y k IHe*h QUhu2Ȥ4z)`T>g\%r]!McXr:˖z'z\G1\\XrEE[^CԵ|BmO*g~Q:@6 m&}l%:\~Q$"6\rbP;L,R 5%YK*yzH w ۦFg/ZwVĻ /Fg9}6G%)13"Z{W4ժ Xe<De0SY3[,F"XWhDROH/N)UD(䁓R(kkP*4m5{)E[gofk'/[١#ȈX #2?s(P1[MesO^4e, B{*E;OBޔvY(ٶ%ͷm{_S6&JGfOj aͼ<XOFd~֪R :suƥvZzc05ƳP:c'j᭷va[4x;q5J2Z>5Qcʩ>Y=tRtSNa ݨ\֬a&F#)cYp1tEXOχivtXNd_ i.K"CgzXHS"C}򶖭eRuHȜVX7[mB:G "k&yK=Ȋy `k7L-a7AaB|8msc{giZ\w\fTn*- H%W~zGdڥk+tl=d7-T[4s`D4Ͽ~(BCWE>*ҐWnL0P<:h~ɝ*zԁ[侊wVn2kyg0D;qO9*M#Ic `g'[@oS 2-cTʖښ#3 T0Ti2x#!ތguf];_ 16ɤ@j, "!A`ld`.0J̪f kxҐr^`b}FV~<{|08p" ͖ J,kؔFGj FǪ2qRM^ӔZv# 0_:P#+;!%>tu)o(&2ԡV[~aڜd˽ ޱ?5 #_mĮH`e˭K4+{V%?.@)Kb1L4W^7 ߔhED{>].ܓy@ZSZD'>liP76}xRRG#6u D9~Ǡ= q>i!C`dv7R\gّXL" d̍1Э`vD2><;p~- %ٍnF,`3)n,W}IQp?{qX:3-ku|T" }D\i IDy KZ[TkWtHgl|qp6&Q IkVVE[(iu#,ZnOid`c]n)!,|8ӦuVYXlHo׀# `w\[{K dFEFGI[6xk_I{{ޏ5qهCj?@Vבbj=sD [`u6_N,͘l:+l-f\~Nic aK:szY0 FޠWIN9ݚv]!tP$iH΍dq9yC4" =v\g)5qZ[9T+{Ht)jf]ܙi.O}_ݯ \RفXv,UҘ$"[偲FFӸ?ˡ`dϙ}8ٱVNwS1Űh'B)g79hMllƑ}\}ڜx1׮sțLwL6TM>m+%]1iS[Oy2rCx?YVoN2,nT?iF[&w rhV[M5p, dG^^`β&;B-}4cϏK Vӧ曊@Y3H{3[Tޣ9d~[=Pz2=t:@=S酏7? }(37*lgn g@V}k~ɏߊU$ɯIn5dT,Ko/;j5#(L:;<܅QI5vY;AidDew%p %PM1s4>~K'?] \Kp{tnwO[C4%_0ޝv_ #;3s<>f8W $ X|?g˜Z(xAӏg]1W`uC7$9jfq'ExNO}.AeTM>Q[a'Y_S8;in Wō$*Y9({\V*݅t48E1i);A-U^έvn6kB;"z߉2l•2>t贅l"vШRԟ:L.^B. ml,-^ O*6^E vBsEF6t{yb\ǭeJvE'O="(ӆ64&i͏Nq撰3$ u/'y3[[ʛދ7{}OW!JR$%MoBjΉoPvr=-^YzaA 8yqM2owdDZu4W&'Y=,me^2%KV/-+Ȃ]vXtV'?["O].ƕ喩RܾauY986PDH}gu^gG+6_qǕ2zj7֐ے~_|o @`G%~hPee/]Lܫ”nK~*ŘWCTs(Ё轌rltߖj{$ u]Y}C߈t1@`x,`h9j= m.s^(>[Ui~e`뼌΀Y`riiTi5 uz+hE%8R~]3P_0W& @YZ5(qdSV2{2~j{bՈC>$FKzpyaig߇lΘa?#;8O`0zLg؞v*a(ZlTr|hUϜmfa@ KH|v>i=0 ݤ<WE=/%ә X?s| ͮPXI B73\^EP P"^7d]7bTOX$^uBrJ[VU3s J>`HB:UT@F{xǞJN1M;F:71kpi]^U!oP8־zG*/x؁@صNEFkzS <`ܔbԘ0, b[O+g?d$< OOMZy+MQZ-[p+#1j ;? RrNv:NLѢa2VR.`*cD Ŷ*vW5X3bݹ6󢥰mc]~4U2^dTLՅO|ħ55*~#V hEZ_E'G5xE\D3 +߅$CfkGəWc>sH-!B)Nc^'zE2!y\f kJioXSa?kή8"泋QLI LO=,pSh/QT?"tנby~Dd}yIV~87q'S&j9}~Tgv}<%̙<.AIe(t! "8ŔdPOXqq$R*ݹVmyvN Yzt&pSy Z=~ONo癲ګv$櫠(˺s/ sR:LX \\læ؀bYsI{DUV َBDD^-k |I kχ>r,ₕޅwp e ֟hGX U˰')v,m0񇧻FGrm z:NsQ@^Ę6xX?omoTb-?&k}SItN3}6=QwW񵴢;# >vSe%kWCwsalw !WikɷFk{c 3RcZ*LK*W]$ޓhTky){#Y \s1-h vb -:o0ȅ!X-LPW .`[Z C[9IxF'|*3M<*XEcfʳ+}굏+4FÛz'^nA#|xz{ DŸjW-l,؍ &amsݱ;saZs taGk>9-.RSV)S#Nh( VD 7JLnRܧ|jW#>MBj߾&jϪ0u:o(7?39^;eAgtع R K|HkC|jySq8;ۆG"M~22}^OY;O{@v蝍r.NtF3?۹y8MWyE 3jf![gi:ʩ1]V Ո)yK'4г<~r'3?x\qd-;cgh}b9l8fǑ#'Sr-yhU)3\fNg+>>w.r*dq6?$#Ej3Cn`uឪ<(KUF˕?DI#l,0 H)[8կ ;ZO'|yIpsv^Yt?8\)U)_:O0iY»w[[0ϦeB4jqShO1a)RrSe{Hb xFpw0{6ȏle;:w=e׹ c!Fck+kOuT^nZɆ|nQ:D>u҃sctn(@HeoOcV/fzQe ?=$Xqz; #$OKNblfJ?5Y 3n F&蓀Hg+uRg>KN;abkZ"ړGS8mC*zM4sޮ0:Q _=N.3&Gxr+ mWϝNBZK0`ꃟ?:O# }B>b3g vHB7'MlG>d}w?X`74$izj-wM0$> X 7 Щ0.!'@=+QTf-:>τq=eBHTqOrIal:?J#c)A^YyC߉?, 9&G4@"v#J$ޞݶrd))Bo0'Wǿ6:J@>&Vq&WCR0lJ|Ӕhrf̂ҹ"qR? U:a|  m+ʺ>ZPɛ_1i,ј L+Zp i;EZ"f4`Kkw,%;@`LgB@>?GO$o ZÅAdb,DݎC v8 ?b0ְZҍGk%3cxPX('WJ"sޯ\]P7фyyӷ%fY4Nq*'#'0 bBg\VHi4٧ѶK~к@̮rfB~sLBF˼M}V*G9tyXY2Fo90h"J\-{(F =2}ي=5*i֩tֺ!Ē;{*]tS0&;q!g"rIrбsx\8LkL`s;k$ʒpo̼zt::'461ƿr*U[щV$wϪPo,PJe>YC@DiÕkz >e,SSQ9V?Jm^H@T`adW:/M C.~=K~c~,uG$2 X/0_ z߬T}nBx(AKfKzRSk[gQU1:P\v-wzmPN5i$!E#'}F}<1 ѯb&NkY!nXldww]Ǥ}qGeƉ$p"jwBK?'f]$EgOF+O{fтAf I/G_o%ORoQbC_y4@* $cz91 ߀{ s 2誉g-q-f^X4pkKF/Zla>Չ"х GC:tqr|Z=bS?T_v[q:TB<{.9 D5\INbf \_kW;Gbs J|V<s%Lb?3Q*Eo,/}K{q:QGdb#]`.(_X'hI J O'A'53%}9 4XZqˉ{F璓5 mL"{fqzsxAX7ոz~?+kUા-Q6 |XIlmq**wMg830[@ʷ(eY)}Hd(S! Ƭ6K}&#|XIm:F8ka=yxwRZAS{cHX{Ok#i6yT1{dX]yNtXՊjTmrб2gS?+UMd.n՚)53"Aj.4(I9zF RLGl^ h “Qz8AH݈-h1ck ;%0 i:_oʮK`Ui0~q!p0/(ᜮKh {W&Mj j q]=O@%tLyՌ+& vuM̧b+כe5{ xˋ4,Ԗ4Oq=V|r&͋GgP'ٞםn/O&B0])-Pp:R=o;nvWs SԴmΛHJn/QL{m ™" &keL±(ӹP\Gq+1̒Q!^5B\Y>X@~VŇˆ~wTߗ . %uye 7~w?Ew^rc?drO *D#{GX$5%hwL +YaߚZL-ƺ 9ʦi'V((e'+3,B: a[N5춡Aϸbʅ433>h n瓊69ޔ˪n Eh4pcܐ!n3GXTY5 Х 3#JUvXVObNZ5[ڡ#Hs J]NR&Ed%< cl$m(1ɫV(tG'3Q M*$ ثث!đkLa\xJRZxvn(2PXڸ{ [?K)@1"! FS[֥[ifG^<Wx{Vc]ܧ:WL@*S~&'sa_{-zWD6:z};Mr^33 "" 3x?ZQ۹p(aPpJB™z2%+$71qZ] ǯds½Б=p7ݠA/s9tl$< m Ci0nY2)D&pͭ@$R7~-ʂ,nƐGp})'䇥XD{*Fi;F+07 j,<2F?ZSLP>.7)}*z AK|P_yb3ghYfb熠GKn91:"+]-ad yoEވֹO:([%t$ޥR.UT5d*Ha"p[ZK;: xbZHzot̽Hwpjx ծjN3Q$4J!k<eਖ9oR[6 XHܣ7t= NnhU+fD-2c?n(A%04(d>}r"xK^GBU'ud)Wgֈdq~W~YscػK"`m~;LPE7Y%HƏ/+ 2J-XAs{ג +g>:UnNk. 2'/,%^XY&u $d\)+vA ,znP# R@aIЎ|jb^`F{P:)ɥv2z V55n()*B"a +@L@M#fX6}!H[*}VWFEEA GrZÏCaTM m5O `a)4q8,h\mӟ f5"|,f>A!xvYz{1v1dbƸz6Rz%hKNWP}IU~MOܺSORpFRtuAvRO?lO-st䔪tObL$7KP1uT>JKv(Y<_jM,xߣ(< sk܍x"n8:Y)k֚,׉pTKr{2^T$ayM!'182Fi/qe,$g|:(ʼnI>핮B|whiU̞{SU5QCCb)"w'JM&C!HĦԕnT1Jlr,zIV8K7U3ݥ q2a^yfɰ(|k ԑ{kl_7}1}ܟdUNUpFE8%iĕ7þ3ioqj)d4\PSU6& 3Q Dnxyy|ٱ oVpU[ HX.]$-J{t&tޡIА#teP )Og?PƊ5!jc~"' kAȖ6#e!P6&m&8 apKhϸI-*,:eψXAx73vAU (Ae9O7uvh/5nl</wج9Hurtx5&mu cQlV;J?V@Xa\ffGc`ì)/ Zm_$-AiF9s^OϬFK8b W r8ZnŒ٭15~ojBoL+1ꀑ1j(< c{K=֞6wZ tL^F'Yb-mB;c]?ryEsJpuHZJ(wxA(bCy"L3h:"@m,GxT 55RJ WY1)pC6_I,谂<Ňь8a`y:)%'xQ XY=[*\+{XA;$7!.%^"s҆ي1 #?  `b=ɕ Nmj!d6LgYT+$%Uեs}kaz;Ry _Fp> stream xڴuT=Ltwo4lb - %ҩt#-!)>}~~y\s]^7c@M,nʀ,l%e 3; ƅDM-4ہ@/j B"@gi(Z>.@v?@ f0w6v@zH$'3J%X  /w;@Et ghښY Um5MzHaMpҖeHhI:LYmM?6L-wHtei-q-}5ivkHlYC ϰXYA`H !PnoӿUJA|V?*oXe  ` H??OS7S H??OjAkA.:?AfF\jAG@LFܐxK#d&;dIVAȚ*@3@ir@:ZI =T`Էv;WAH_BaT>.@" 6 _"__BdS 9%!j$ Y79"qZn#' ퟫߡ<@;D׿ц?[C$Kpv~=n_% L4,lkn R@%@jx!d=a)H_R/9H%_ۿZLm5?9`Mkgy+Dfmc!vf \eKH cy"rAހȵ?uHK K"*OUQT)$Zʞ &*ܢДB:>~i.mH&->Zâ\EIPPk&- =z$}1yNiTΫg7G,E NũNhNByìYqިknR[li;׵eCxw$PAT$6#$<~e4بI4.b)I#f{~+}1 铕 *,'Y_v^i ҀĜc{ekۍ'Q-%3T2nO H76&<g2o %^EfΪvI 4BW]o@_`:ͥ1x }I Xo}v@o 2xNui˸kbL%0!#kh+;#G ^Bp<⸁ZYbɱކ#jtfPV4/*6uT4; }ċ"Ht\Չ|vDqEhLd[F j^8CV6dp*|7vr&CY!3?{֭q{ nRvmsU^דg'뒢l Y{$\z3&Q#89uW S3iWl0M|Մ3YA_(g|o iYqaHop҃'wCl;ew_`QGgpJJElTlx׉\);KOI\W$e|;љ 4]Qcj~"ox#ً؇;=b"s yegi>=0|5]=QD(]2DgǩaF8@~e:oNJ<}E$smO&[)f2 >cot*A3JM! :8]HZW mF*yUX<:h#ԂEKom T\Z 8婊Y 2*NO.+)4ͧʆfߞ'p @#S<`Cw7,yQ a#!$7b/7q<=uvxilɣfo"*7u%7=;mHB4 8:Kh-2w"T0eUa{3D" f9YI /KUsXS"KlZ oWn7B)ӯ-dl7 a q9N99?vth9q\s'bey?Gz&> vEzgVpF<8PP5dXkyy~FUDŽZ^_{}׌2{s1ɬ 04d>t![Z4XM/$F[:dgCњc6X쮎I%;p튳ꂽ 6x3DL$;=b&at1]yla*uBO=ׅvK2LKTN1#*έȳےiXsqxiW>as@ ]eY\oh FeH:)s3\-C}X<QR=I9~P>Jۀ2)pthkMx)]e䍚1zבR]Bl,*3ncJcXل ; @o;tGԓ~vRՀyv^AKNʴ=n70tVD$ )[FPQِ+k0'\TdיC>if+Q2fz尋4]k.hX7ߌYHVx~x/=^8~JgX)r/#@iʓo;ӹ@^J:|{1"3avqeZldkI~%מ/Ø.K8ljs =퇓B;iWڭX>uwmmWX.Q{(pڨE 괙xA3?ˀM)^ n6̝kx&A498"0j␆4RU6 8?/,+R)X_Gj댒Е=^Tœ`_LXP3Lճ/#nc*:kYN Wh|:򄉇C+e+ \ĐέUy ZŪ8%mؓGJNĞA&khWE/yK )N԰s#\S6'_+ эq}d!;,M(J4Xbr RSi7r9TW*6;AάSBX} |R xSӴB,qUR<l]GVt]twp&,N K? v/$ cv0fUȓ֣7";[#ρƆw|ZPhz~8#hG;=DA&Ot>aiGlҸBGeDj0Z/NQ ܒUw_P'yzPKTX#>#@fM} Ԩ;XtJq&eo栾qLh8ɼghaXFz'<:f2Wc %S{6wP "6u7Ìb]20]Fhq9-J'emRfᜣn<]2'bZPagiw{aL_+u(Z Pjw0+!\vfeC}r[cmbQQNzOi='  PcU!;?_CFnG\p4e" 'yءKX* ܇pGsb2 }ѷP1̨4֖A{ZWoxHP8x0eb+[~޾O980LYbEW u 1c ׆EJ qy#l?LDK"^X}kw'zSށ{tP"ڛAk  ѕw3 nE{Ft΢~3~FQASvO_ٶL50K`~v瓑= q@=_qO+sX`ikg+yp0FTLO0 "Mfr.hc\HC՟tUv} U4AD"dКَJ [s^j͙ZG&;l,S:ңETR7t~(a1NʍyOP)m2U$ o|>'R6FdV_ɉԔn j6(0{cڝki&*fwC,͹hzaeغg[MaB]fM5(w+dz[#c㙁u辇{iXG9Y6Z ]7ÐzKjM㪶a/I0>!swN^܅]NrmX&uP`ix*B_r,Q(%)o-Fڦ Z"x;ṱuJ+QZ1=Ag_$0\cqVa Q,] ֊ۓMӽY[E~[׹B;g"6 zGQw'%`Qk#I}=m*~SV%:`?fV(ul]=*AA:ΐQMa"ˏ0T#MIޙ3goEe-}74t Wd^Ѥ.VwsVz,T.K&eVYӐ:R.S_ui:A@6VО+JkżU(IQ H֯ xzX#$iAs>JӤ[bFio~6zWt-l a%-uwy3^`†L/xet%L [`a}hU]`=X]SZ5Q:jx5f8ʡ{-;Ht,7U])?h&V<{k* 7D.1joJ֑|ZC* /Ut۵ TGŭ66E i1}`"ِVÊaIp0kv[5p=3b=vUƧIkmp} =V`Bq pVO$$' $k^( 2N;!}C yfN[fՈ]N]|7tMw_¼FFH9ڔ C^-][OI;^c7!,HJ :h!Pk >ߊԮ\_Av2GjwႱM&^GDߜ9:i*(QZY,kI{ UIb qK~o*њ'ufT9iδ-mz{K6e)] ŔtG=7W\߰NHE1`O#T^xV9#zԞ1ǚ8q>oVa|eۋn_n>@v}O%9 a@oLlC>=+Qr3Im6컏p. z-o.,(&VгYÃC?'\zˠǂE+[9FdyX^HxSo6̎0CQ_fQ>CY/[SCm/1HFA31&|EBiw>Bs(N:sr"oD$ k :^L>M_Hq4T-f?as'L3+1yVَlQ_Ȓɚ?=U;^yΫZ@ĉ1_0,Jú| KЊ8ʔ +"`4'CY- }ؼ%s ;)lwҍA P|#^`tbzz1S:kj;H㯋Co$gnp%?H]M-ՙ&us/0W$KyЛkN7 Ѵ"E7S_[^1=)Une;&kn+F!52]KZC763gvNX{H"ЀQJ 5]ZkMu9]o;zHL O-~jqf X%JB%H=?W`y0ȕJ "-jD_rQRAin OYmkA>ŽL"# RG=MbTrѵ6wEЎ3U͑Z1R XKR " iCJDւ +梾Újkv*PT:Пf PU×6}h?wu6UU)^.5|VL|uq+BPZKYeI&M3QYBcXl}oYˆxbݎ K$Rn| JcZ>* 53 7P/ܬ&`La@#?U$0Z sm* {΀VYVjyqp4-zϑbm:ұ3@t^xW*ꭃuQYl˹ xAp}PNb7Ȁ 6&fL>OiM]Jb-lؾ}M,_GC WBGPW$UX.M?1R+ըQFRx`Wч( FefP,*8ʫtWE2Ɔk(Kкza6["ȏ OcMے s?npС;1~JO[;H8.QXt!vh6;2Q=>{;F2P #~DR/e1 cKULOoOoD/umT)| NzpAQMoXd9CQC#-_י`Eye0 ۈUnܛ5)7Ѐ2)  [Ucr-i7_M-Iv\cDSuf޼7׵l-(?D>!l$zDQUvx'z h;nOd_uݲ *0HԣpC?QNYӑ,F59`Pc&hȱw5uw&oa[4DzB\~#KG-dLQWfaР/OM(uLxť&"BcH(QPvW͚kJiIVl-&wN|Y":c?ձ ^KD96$k7Ogf{Ku\ "9u]7e\(:NoFYn5dU)qd/ vlbfdTIJF,9CR|s7.hn8?hXbYVpκrԫZ P.Q%Ѱ1B)I >=dn⢷!/\, ϏH,oߦͱL!i-hV' ޺-¾^<͑{\cjcI.Q=dj֗\| ?-ο'@bK?ū}x;7]51Mj='xoRfj凟M=m>N<یc@LrK10vS2 Ro`Q݌bK``Boْw@x33r3 iH*)uƁں[$Ḓ}lbi3BgύO8HR½CnI"5ܺo4&}—q>|/J N+M06*"[_O@ԩߓ!Q"}oҧޓ֣!$hN)0;y^hC:F2tX/h225.\e+9/ލcGP}7dB_ 'R4! D^Ԅ%Gzrn:EØ:i52̵/)Hfɕw땖ءR9{ 0ف}$@E6咙 C& ^:[(2Qfs_!@үhڡ.47hdgzr&NB?}#hs(&kLrTژU/WǙ:'UYͲ`$7PwA܌ 'mA.R%W)܀YlUZm{_# /tf]x (U_ tqOZ)Ϙ@W5\_rwiƗzAܔp<t;5=_kv?(ٴ-9f-"O`h9͆1FKIoe`S% cM0Up;י]o3~ ؉|q$)(!P.Ġ2[Wh-`ӢȸdK1M82cH=[`sңJE}aԌ\mS|hTS`zTX( J\lz G 9|+7 H19M9"I[>}⤌>giSv?uWFk޶50laf~@]L~=Y~?F\_޷(tmYq|"h@E|VҐd#/X\?ރ޲LRLR(̉LR@"tS*$R [w`ELKwvGGf*1E`T +u|;yC'1s!ݧZleAbTDNn׍ϑ'RЭS*O*D#2A9"k# c;bl# ¦}-s1u/H%_~2'OYgV詨p|޶5K"d _Q:jBoyqpFB>f>u?K)\\ǢS9*"B86a@M*O]B"):,0 o6Y|Ѐ&']ZAYJ 3*pQ'OL{j3tsiޗC"%qcs`!РD8!`}d6I;]+D9EĬV ZUL%vEPl2c{'|W~kiXw`/yWyqNLOِ}mn &V+/ZrAmBqH>|_1ƠŌ hdv8_-;$EIr }sy >aJu/+75}<eec}&h@WKd 44xSb+Bq́Oѧ5"z^:t8?^}/kagBns=w+u0fT bw_d_F;UdO6 #4etf`(JSUk&iY̴VUZnBϾ'"*"K77VFl\{hRųV7G`T \*se(M?30:e6ioX@1rjxGR}ѐ鮜JB\9[uL[Ic"Nbd$ң3Q dl YfAJ&3VR(ڷ}·:Z8xMmaCh8J0Z vªxPWԂ`A‰ɇNsXMyH`>{*6,(RoF!EL K]uBIqM91<1qB}?`a /`0@e)y-`]eh#X?yjHxԷŲtjך!]3bt=nܻfP@|۟>-GX(OdGUy*m?˘NѨr'$AR&/+9wݤH,ͺ>+FZP(4~?pBF}IЧf熖+W~FP(WUG>?`76]g-}a=+ZؑrSwpNqCQszϧ16wTD*D)[Un(x$CMuDop|-129@eg[^~ΖzE͋|S]@Sk G" ķկ8`IU>֋ \л {0?xGT~NS$z~U-s{xo\a}x6#sʥ#8MIy~QzI(qdvp*6Qst5ŶAP0(8UO.$SG4"/]y%Gzկk-e!7f; X|iVc qO;ش2Cڜt ՎpZ>:Tq괹\ 8/6kc4|q2#Qd=MÉr"ջR:r ݏFyk<<@U7a${2%PXeÐyac_4I١?%. o펬YDxd3_r ?Id8؉?vsD䏲C,OdS( X"YoV"r;z/|UL ;Dp 4܂}`w0Zi#LVJDD/o{m>$ A"e`Q Îz1tI@\ْH?~LaF5te<q"Cūq fG&Cm /ǀ!XPѕ:K@NBWVpB22[)캻@Y-)Щ.,` =5@`Xs(sZx&اsf32#@jtİJ(54A`Y9?xfX$k2i pJfTgZHg% ڱ 㤳O|<#QEktKmL!z=poPx5,Ap3Ɇ90j'W0~ܑ&0PmZO!}Eo) bC -"BĬ74(U~ތ_SCJKW0:^GtZJdЁP?.TWqm#'_[x5tg^P=:)h:Y4b&y/:wH::qSW3hmmOVhU{m dO$WGVNSXf4ߩ ̸#~}X6n,[cik<T& 5 <>VnBLLʿVqbиvj镵1|RL{Xl`2f(֊Fl#ڊÉnNGc՜M'ӏ>$_B/b&46*TZ:*j0Gov-bVּY_WR- Ϣ @To^ÆC ^(aU* a%aa~w$.rt| \DWlUDv!5NbL FݐO$ ͬ,a+*[cLrҎ_],|З8j$k/%MIYoi@sd\;aCv<69^^XngM7iFsF HKk"pJиFf!ۓ(-ܩxnHutXی#LedN_ `'W1Ǔ.|o⋥] H\Z V!wYL4rhri%?⒠ڝ~KWcV'N䋁ޓ~8:<&3v_ z><8A *&Z;eqIMX?ҩ}=g H8@0^[FuS*vmK7~ˊ#<7Y.քUA>0$i($xmmDQ"0>yh)HF5=jBX䙬?;8e&6L2;T?a wwPX"Knuy9o r }e{aᕡx^O4ƠF9LR~ Hq*3颉Qwߥ2*n|fɸ@Lڏ/ZZ.U_)3\yA(<ӇMFHgM5b2^,|G=TOLPC1jѷNŧfm(ICg\/ ]ߙ􉏫-ȼo,WWL%OP5o!ȧ0R2RU`Dz[gY[Cle<ݳxM0/j*>{3}ȌF3  +)eT=/^DqhEzhs=-ҝL,_2_WXZ&`ceqX ^i|5O,U>s*3g*>ic7bn1O^/DM~ a=FT['vhı]X{ըoW1Xũ& l\ E7\-QegQÒc[dLdߏZ5>VmeXK6Nˍנv7L.c>SAGUhT25٬txLRB4߆vOHՆ( 9`ݎu=Ӣ /8D(ZQ,rs;]A=^6tQ,p^??$[AK_ bcl7ѨRf\mLb@j Zx@Cr/[vR 5-fU)̾ztKg跺D✗B7Xk1B3D>Hs4ծ^wU2E92΋eEJ Am:i s~jV !Gqѹ5o-eWo[IRw;/'">nkAIo?Ox٧75MW 1Xbr){؀6+³<Ȯ"bk\ϴ"?GbKT7i6 V2Ͳ:)D#:3cNVU%ØX^5 "37# ^ȗ=\%ӾL ƒDWi dza')E5 hlV. \~ُVdԾ8} &GW,9MDGXO R=PMqVmu4p=LdH lv3R<'_4- l Њ+H$|f p:=*kES$67 bX?J9O 8!D\'`.a99"g&%1|oζ%9e`~ ډ()r\G;da1+q?Mbˍнyo= ȥk~qtͷA_kt&0fj| 56c1>"~/C(Bm^Qh%ɿ9SU>TK'5`RKaVeK ܠcn9aLģiͳ3oS +r#`Y; OeeFr3{7x-ݺ\=Quڿ ml%MJ!R^BA_n9|!Q<`[-ws4j6^ȫv$REߜ~aխ6r@lYYg>6=|> 5 s# I6*)ry@ $Bzaᰎ+Y#?ս{GWAzMu[3O`v/͂Ss`{$kpj& ռ]GWOLh'4rbYXd[۵d-Dmq˜UMfGHI䌘6Il۪y\64[Z]p1 {LnUbC{_oPr)r8<*pn* 'ߏwx&C5rkTaHx뮩ʶ8PCv<8#~&=Wdq|O0<̳C˿(^Om 1"L^r4l 9WW1\jik@ͷ#n@[>}^_m%a ̥oyܸN*S< >}3*%2`7QRf[韢V,Z[}2e{Bt^4'\r6yF@ uP^R"|}/{9O4y.d040ËLRjp|@φWnbR6RDOW+cܻ4MP@I>I9%Ыap ǖ W$ Mb8, ? ;c1JlL&e;L[(R.st,hŔA- Zɼ[ìvj-cUNVQ?*4in #7 zdTHpwVl]PONj |'ws|_/ hZ:a6goWoqfx]pQÖh6 IpiJA=x, t3Pq\u̥~s _IˊIy҆k.vJ8P{Rhn&iFJ.cښ: &wvaH'# iمi+ b/IgAFOBWa ^.LތrPĸW3rt&^z?[Ͼ7Nnr#^Nn1|ɬ$XN$w4m@MpjTZG9׾ͽ80l3f[) +CwJ>O x'k--&v`iRvi"nU ^ޖ氱BG58RqP`"=Ct5OadSMϤ n=&Q~Tq&TY!;g"uLWt}YӀcHt_ԞT QKhHgjܵxna`e"LV}H.K+,RuM35.R=sMdGՕ1KaOK/6Go ]_kGOezCb͍Ю|>ǟ&C ;k/xDmNRGI{ I7Aq1bq{ ŸU߄tFk oխdu#O>ߤJYiG60}QQL,#ƣ09-$5hg :;֕``425,ưJh`*)s^% I1txf1]})0?Ydb 4ˤo-, èX1 ~rL;JyMO4jC1)*eZg$xED-i* a&[3P3ގ&j[>~)<{xk =.(X 2Y\ bܱu$TRrc*oνqu Y&M i@91P+RX˛]LVII@U:]'z _ux,=_@fQ*M LuL>/9:etk@GWN"4Ϻ̚ONII@J=>iHssK!Q/ ^%&9G1):A\3 m~:eRE7c AjḛY:)|5FfR_Kd2N8?UT[Vդ95Ƃ$3(bBC%%;}(R јDwնonU R8#9; \f THj *eYmd@j$uuO$& D5_" lE [@' qe j$]E9~f(嫑N!(滚\"yRPXtMicTqѷ˪~Pj*~,jUCy(3`;p{P#]Vwlw=+Khl<B3 -|SN 38p1 vA,!ޕ&Bnyi3\%?ػ'j}D?F~scI\|)3׮`,'dn ܮPt I) [F#d9[cEnז80,2'wŌ1<o!݃:eyG<=/75 fK!ئx^sBqsjC'{(2NrVOB{VW0kΏTއ t[j;C-(Mw%_-Y3%cTna\, ^Y®0A 7tdYOA?`^nt ɢΈ٤Uf`Ufaޟr߶MFO%Q,xYU$>{@v%>_pbyĠAw k B:&y %~v[r^%DE[TWEz_ FVRR|W6@'|&WHO rqlqcSM_ ^OJ,+=^操|{hqr麙׊g$WAd{Z\-*0唉ꛃYTN_݄BK^yiYC)%Rb-#L5E?\uPuvo%,`8,ܮPs ߋZC1ĺ"[yGgG .kFmB̠l> u79 *?^nc. {77X8h}ۂ{v| >ʾ47'DT،:)Tw&GMuƭ*biUMJsa3ȼ }FwL!4-;?,\| o°MTF{}ަA p4]t6cyPVe.xƎeo'ǖUKU"(OGĮg1cx%@D_Y88ӵG7FHdj{KB¼‡6GN~Fé}@Ӄ %-ϼۯFuHNH.*Qy-Qݶ\3R]XX=} 4 (@f[]4$γ$aF 8wlNJbmMYxCMBHc3_PoEΎ&;<'I2GuXU~IVTTtܒס5 â O-m;W2S+Joznx>Oj#/:H?䫽%UEiOn%~ o&wQ'ȍ#c҆s:r~>긠wyv0*/9M TDzW0JV9t""lxʐ~ _&S[Hx{XYm$fb_88-9*'0I('lS*gI$8#E*MNY2ůկ-Űi2$'Q$ PQj x>& 6..XaPX8^r3U8 Aun5J H\47ȍ>)^ M0^jITTC R72ca 3剿HoIEreByϐIB2̈́;䙑T]a0b=/2=j4^9#BL裌a^Eٻ 0Tf_6STة=pD4C@ b&ޅ XaXa 5]`Y!8>"'O[jշD!2  }q}q? %R'LSՆNou;TCuA{Oi]yZmn4U-Z)^&xnZl?Ps*{ؿkZ H7iAmK=ul @BDhg nKgށz0UCestݧ|"P0||hÜˮC„q.ּGv➠Aq{7XOGG`uZa|Ms4`Z)<M#R,`p#+>t-+/czYkΧ d3ԩ(A yZ71O8yoG}svÝw^K{zi?z,RA^2=ǃl__ J_ ' JwU+|2=Txu pO]8C;Zzo(״+ {~uD6)viA.QWJ%́.˱=]R~7-6#r}̯ᴅV,etWK-r1 4SDUf9,w9!Y5 ^=b4_ASͺ Jnx?hy\MyO 5ܕ09RYt2Q<޼JYXʹL?;$w-5d@>CϘjO+b]o ,A,?pkbEr%Kũ!U϶;yPfPc^<ݏ$[rͭZ-o &A@S`-G+iKpf5 cwq'`qԑ[U g}'s ҹ(tS2'7}ȅ}кʵ6/is tq5j P!vЗ̦尃]iDSD̐-84 {!HWO,]~tO"̶/ QlY1-YGgWųqco>JL,x 4yK,ڭWذ%٘؞AҠh\. puHA3S.gR#w^MkX` e*72[q[j1ITX*lEZo] _S=:)2 s4t_=-rCM^j )O4-$anP\+}t`ED(%ciUɆz} ^V6Ϫ7L+ G\u!66Jve߻PLe/K\wA'm#'pN 0ƥ0F C%a 6795NIX'* USu;Im8o*.a+3YMU# OöQxBjZ2wYeY|a y4uly,ڄh ;(S8/r//iV)/ {dӗ'('o+H;༈U>cȒ-ij W"0&?/aU# !k6m nl}Vwі-bo^o28ֆ8Rp: F%F6Dy(pȾe' g5[O>[`mƔMƼ+PD#5wv# AWZR]+R%u;2$ua6rXהb1ʥC0.``[Q3U!)(fZa6@ne q e+j&uq“ĠnvxK/E,:m~L{ju> zI=5u|bo VA:3u{ɬW< ?]Fmn~gRY dD79$8,ַ֬wX"+!w-m~TqC3Q澣ue]J_m$`;u=X&EF(^%?(BXmc hIFJ\_:K_"z 2* `Q%hPr$Κx*g ]\ n]LX-K(n4l>_u#yYDO5Xqd/x{K-ߑꞃX<ܟGq3Ma0,‘nkF"ʚװ/b ex@у(Ifq;9sU ΩՑ9?.te!߾ESȚQX#p-ݒlM8tfo TX4|pcpW۬4 #ew} ˆ8_׭%ᄑ첏MHRXf>V[7C`n6g]Z2 6vLQ@Zd5J伸G1zKѯDO+u/+靡@YDj0$S8[6U06 u3Q6ͻ>Qokp# ?r=YF+6S)BibV{sТ,_K-u?!1T2m BI <"m:k.HpϠ'wQId dwҌ(Ξ*bvv5wW}ڱ]G0+ǭPؿn^D#' _E+@)vZSY~'bޓ{eZ:8şzZߩ=fV,ᩀePSSٗCoh3N@w':Zބi}z x#"A{`+H㈄)Ma! m.Gu';Vz Q;R뱻uNg'pc-v`p #yU*ujE]!FM]@^Jc_cκ#zF? w5jmG?w%fҏyn(>X퐌lY/'//áMb|P''yo&,ёZ$.(^#s=0ָ `_>L/_*k/( @(a_c Xă9 `+IP@E!ȹAҷ_]Q&Y=L0yfh)yĉ=%'U");G3S@ޝ%}bS7>uT~yC/'Spկ%ğinY'"(,u3 _i!:oT`"&"&4Q]-G`qFvgHR?m8RI!r_`E˘sDeHVJD!H-T+BȉT{&϶ى+Ntً\4p3_d;"ˇ ]=vĔU  M{4[ʟ;>'ؚBұZ7y@yxAeڱd^Zl"SR 4]S^AI*@h7;$fvu!r" D*CRR.OF}cN>q0@!>YAH]=*#dWkR,9Djr0KzF :rWc(B|dƉRkb #1|[N_Yev^,/jeEa\E^#a[Cu7֚t 2^ zGxbo44]XEj";^6R5j.֘zt& gOhAk"BM =>Hghi=b@tYLAd9"ӈc5EWDZHnI;Ω6h7*jP.yՄ!0ZUl|d4k==2r]C9"WYj5(FvRSI&mVtT'a|.$=%"M:p`"s+nR6~edr5X[36=2 YDs% ^ `(5hW/2 zi@tcͥofa.Nv4Mg=fNY)?!Y h6T,0 @׻b;*6p RK 8#HD$p !:T8;EK!_{{Ix}F #Dmmi+>,>k&S> stream xڴePڶ5wݡi܃;=;w Np>ϹW]-cXsUԙLA2`Wfv6=t:8Xظ%A@Wk$u(E#RdA``P5A:_@ŕ- IG/gkK+?58T-:xZ`s<" @h sLAV@; @TVSȪ)kӳVwsttp.L)q% iH U~oPx']QZC\CWEwōojo5Y: zxxX88[8Ol x{wفmV+k3'I_NQ%]Mmj+G+_ ** {5]n.loO9nz(m ||c@?f6sX"`amϞY)+Hk0+ ̬60_K)x\7J%X ۜ\Xm`lngn`k'7Y\lif_zcfc~#h!AWg7?yfoR;.U[8e~c_G휚; DV%7IsDf߁@{k; mtJ@YX{U]ͬ5\o[ځ޶/#eݷ`f/ߛ,l _. Khk+2_' 6s0[8y@gg"ۛ8>o6y%+ - ppF<V?/;Uo`}7JF|oy#ֿ?Uo `qU;OVFߐmV^V ?"lo]CfekwX?oJ[;-=7..oo\A7~c 922oS?*п.?_.auWg[?B֞lo'OрK>\f7r]o3\*o_@ 3ĥ3r?© Xj~*\N"|- hQ`k@&M{C@puZ[r䵹6OяUZ|4WE3(Sq1@>@k&=9z(>c2 }5֣xshq ;.+0kR;*+^h4 nd/8Y02PJ#wzדj0J.Oxr +L;鋤n CP2X|uz[#;֑BaܤNu=QKW0\:<]v<+& )du<ՠgquZxԐĭ| Z*Բ+t%'E87CS$23Gi.5OJӏ@J#qЋ ZL@$G^w5w ՠL>sln/raʏdɭ hY6ڙPGF__2S~MmC q/N:f.H+Ʈ檉,u.x7UxS7>}vJ{B/l*}ħ}~di&'qفAremK[xL88F1}-3eYeS*k|pb%Ɗ8Pm0F y'18$(rqs۔ъ#B^ÆJQ+raS&20d13݇9gl7v3ܥ5[j5OEc I MGԡE3ړ-^!)KKG/Ś1 :|u&1)Go~d~D o'2uBṃa:Jށz簾E U ?<(5̈=IL}UV>Fo^cCRZlÇV½LzcP+qgsY?&eFv5iUO"ҍԍBsnxTBRXnv%QhzI=՚\?7c0K\j\CK0KCì}J*E0~0#>lȏŊ,$[b[3ES, Jpեm4l`BC}=‹[ѝ"}^tӗdb%ѳqՃZnAte?\9PowTnP $e}s~S%Վ>e~o2d>ŲgE^1伿j/gI-3p//H+1{~)uMKخJ >ܛΊʏ,m>Kܥ9KqVai:Ʃx}xṬZ&5c_{;FXU90 3g<0al<þΑՍG;{C)}vY$IJie] u~͊Y4ln9kTv|o3uY3/WpCo]jXOf0^[A/lݭRoIطV#tDk jPgFp[,dTKz{[^lw*p-y?`xX]4Wmb@c^ ̽ Qg_W,u.Fj4.m 9rS߁O94Wx:3뮚ND:zJ S&V[AhUPA /ߖd&֕3Zr{8fZ"xDp(w *;v U\)"ܴ,7[A_qn)#g>(k)^ms[y@+Pmډ[.]v#,^Ty9*HFwC+jq%(33VͮYlv[F`o&A20vzHWҜ'%q6(DwA>ŴDlUr96r-Z<@sbSd)gRU ǮF T j;P⼬?oȤMnI0Eݟ]1K9c~,:=Ä0F0b9 D|#7wA3s^'ì$,SFQ] Of'٘ ~TgFÀ25R Weݫ&6~ J寈QG(V?ihK⌤>M~_~b"zhE]Ɗr(g_94>i6-TXпqRˮiv_NsFodPb&]1}|8Q9r:Xl~q0(DC|k ,JYve[vM&p kFyeB0}?EI'AGa;SJ9()ΡFMT1fhe sMfBuX%^X%v* AUZ89;9X:}'3% "楞ZSUଫv-BPӠ]`W8ƯMo̪Mr)hw' Q@|/"m8GQBTe )B.y! LgT<pOHΈttj.MqS"O+UhX?s'Ǔ2%AOX5Ӡ"=.X˧&jCX@j;Xccz9Aq_3E~ޟM6I1mpA΁JrB! 807{, 2.xU&C1c;?b$ 9:5\hbD5k݂%RCB<y c|+! 7 wb'F{9Y$.Ww]%ӊޞyn#M("MQR8 B95ۭ JZez B>>l;SqvjKG?֢MV:&A%;e5;8HpJ9,gC dutg(QL^dVBC庘:.=O[sfgHs.: W^k%&Maa:؊3ㆾڬzci`8Q}H"@o`mePf~Z+26wX[Zי` msf< mAvnJ Qx[M+{ErZ;멁R0)m_̲ۖ5\4cد6q΅V|f*ǹЇYR.rrYX*$@+;3阠ÜpL-ߡNV_do WMTS0kYҭSE хI2rRJKQ-eįO(B ,g}nk $uo%/B 4e>?ec#7A `0s65C﹋z9*F3Ql$iBheҠw4\g+$:k7iKGdr4kfV^yi+`9m@}x҅bT|Wʹ%i+EaC,[7PI}a'KlE; -*ޏ9Ah jY)t9#s旟w ]ރiE{MM/TuM{\ҍhT$_EH{aPhgc?['G?ЈF%(!*DCVU@p'_'"eZQ%Nl:Kl4QOTp]3힅9)Ɵ6FQ=2JGc?.X-buBTo°2pTb4T"|,}?Jr(Ϟ]nu+e.\MDCM&\Xs@g*&iЏ}n·%1=" Ӧہ=oa`!~>FdM19‰PV m?`GE=byͥV&SԆґΠlDhP$:[1dDSWq^u 'ib$KZT4VL->IrWa y3/x"B_I& b>[jMW7Xq($hN>Wj{OaCOɅHa N2qp"> ڎ燍rOEbf18"&:p{(|غ%!~39hV9!wѢ]yV$M}Ƥ8}扰DAWPF}dIs+[QFXU3mV8Bb78NM]Ww_N(~2E^ۄ+6G⦀؀P{r'%QCL)lr]x{.O~*1\,ŐYm-dGnC_yPAm: %l.m|=_Q5-!d3-C_lban;x;>F I quyV%~SF^{ڀP'1'l"$Cd՚k 3rM̀:EkOݷĬi/fOPKuus 6lu9ĎQ1p.]1PS=du'2'n.+ծ 1쉿5N~n{yq9:@$|iG|G>i,Vr).$~YMjGSBm!Cua WH-)𕉕(f[Ft|x=]>r m;hp3n4d(%SŅր:F#gaJu"5 ybpQ^y 9GLTI5`]*xHϢmݏ!Դ)q9&5UD} ૷ХDp~>t90B-]d9|,l.I!H/߫SIuY;;E!bYћ5 LJCdxJb@|klCX_2qD'l?(5?v@Ж"AgפX14tRSڍ:);BK5Q=VIJ'x慁ӽ)S$`K֔)mLn[zK{YM>V y Uod֎$!Mp7hk,n!UIQ1ߛ E%ëR鑡ߟE O$`yʸYm--򢔺O]AN1uUw"#q=gcZ2v\7&t'ýH84R t*#o+-/NI# ߂xe>RWgy dIh3X<<Ś] :P_M<[;qjcs5K=Y[YKBȑXƅn+=_hZT}Rj&D,<*F㒽V>Թopߋ9Yg"]'Ynեę͇s< rdTkIUxn##'Prcy2=TLd}t2 c޿I8]ơwE]8&1s>&S)eʌh>$;՞wͪ;טw.dBV'o-hAx)٫L-V{t[pҒ}vdjJjV|%fM R܌ݎB !W8*x/:[y/r_`رNk mDiPsPl$w#a\;[ҖJ7Ai 3ox$EN7`i ZgW*OfUU Y.fDO͋??a 2/a᧥'%b&I|L9R+TWUN60QgEJ*!/|jga}"fSmxz*Pfjj- HB2Ɉ:8Ia'Fo?Е7Cw4=8P;/ލ~XMA! ' Vх+"75*Ɣp31Һc D'‚AY8RQɯW8CT񾕀zrpT3x5#){fKf\`l*u`X3֑G#[FdvA)$ }[$| S)jױ@WǤ݁Ya".U!(;w>c}S1H0޶wOr~e \]0i1X{[r$" nR}^~O:IJ쩙Sb6 4TGQCqWd J9Jr`H)QFD۬['YkQ J !S}D,{خ}ioѻݻ8y$;WMa%A\\m^zCDG+<,Pxk HЭ%x,0}b +4) qwB3VI϶Ex"J\4ڴۅ4%sFPLL\Bۅ %ud?d5L1v# P͎ųPO M= !$}aqW6]UG6`&(4tdƴd~Ƹ7jU$9ڪ6c H 7gȦީFo='̬09ۚ ֚ё*7a:ӶRjxBӔ+.JOnZtaϬj^U+]5tX_$_KxzHр3P 0ѩ(K" rPXЦƕU>HU[u%%?}\*DIC+0P=7$*¾W Wk gU_N뺨*UwQs/JyWW8iZ֕ ㅃm_CJ3MX/OAP(d~7rr6}%`.MYmozA;e6zm8@9\w[YޅKyϵ쩌(YNIjDĪn`[8Id]]U :U<`kT/Zie2pSXx#lńFQsʼ aٚ2ҬԀ @ݖ5ܷ!%>eY$P>LL$mK2!Rn~Hv%9\;otlj 1s)ÙdܾTOH 6ۓ@cM638w,1YfcR^`^j ?ӯ X9RUˠMvw>ew*O7qq~Rd-Pь-)wE^ }իxaMp2 jGʙ]FXxbR1I!n_46F'D=lh8^ ,|HR3N=L>K^D_|׹ǔRp@S"gF~9V%UKRoi"kҬokES]pr?M1z'5XYSQUY QKrebUD5)GmH&I8C>ِQ{^jd0mbiMNB$nmPr3EA}gМWs7l|ƾ>JHXwŨVAx J {!% _ArnpR5?O|.D.xzzk±M*#a]U5iHtm"hOci>S"vc}='q<up5`Z8N~ȑxBpuLy5\atEɫ?wGRc뱥kajDC 9뙽Бa$ϮjYM=mqj&}z"@v‹Xsq]BnAJHڮ*@˔X[=O|2s™آyw0/_(?ؕb#\8+Rq0oג81_!hYetgF6at((U?0K<Qv NsY4i^ !Οy귶h0s;ў~(},Q1Иo hz'P% ٦ k Ep-VUٛT _} ep'n/ǃfZ_CM!p>5TOi8! >_ΖQ㳆C)' q)Z˟VhVzASaHxvT~cOd<(9:٭'?JYVtOz~V8!=K--VRy|ϝhUt/ӕ~7f^ Ah;x-g=T\6k#o߬HbuŴ˥ 1V$xݪ4.FS2:*ƬgſzfC_)4pbCrD U ^S^Vj(ԑQW+k6P"*V =JdE =.yLy m ޮw 7HNH5 L2v%_ _PN\qۄXN^Zʸ`]i5}{@;{ҊGF *( h,46k؈;:Ƞ*/㦣\"4qfqb"ON|֝eT40S ۨV8Qw"xN8Jv2 ʎQ$9 eB Gkڙ&RJY?e _N>ˍ _uWۊ[ ]r{X+ʹ$I!wԉT֣R顭|1t UdJlUDB@:}O˱۝}lϯs'"k}_gY#Lw(3-9TY.HnH?)=5#[K9+}!52 u,4YΡ(!L_^oDS0*ӄJpe7zaHit`&x,' 2RlD&MveEɐ~oZFR%}yJEɖli{pah}Te!a ͹ڡEŨ<Ś Sam U2ݡ5W@aTkv]wV!`g`[5??talq{0|+ aFk8xQ=oUE|b];3aciZz *rMY0e~(0z D̦ 7n]۾>gi#}φcν:J'%?æ6tDUT"0k@}Uc\-U!jE`S5O@.| | zS-zeJ"Mx.FORv,8d _t 8̉$6TA'GW?)JUkUқ@`[31eHn̵$O煏#ޢ>|hNO_!ɒF2z_u0"՛,HG7kǧf܆t}bPʱ&6#o޲Z͖6B=F!BFRLu=?iB@ zຯ7J.-kl}KW;r_RSLާR^r,k؉Ps- ޵`tGZHdrl4 Qg<k (h̡g 1Ȥimm:'ޓ, }-!/4Z>ج7E}¿}S)MQ.FtDVY*ew_ MY󃿾u&d^]ҾRGcNFo }^ewbo`|Qxox][5U03'=Kkc5#0Y^.o-PPyq2L: 8Ā>q60Zky1'0וr<@T8+pLXE`d7|L}L5u0ˆh2d6J.׻n\zݾ=<ӛ&Hn\ʕ_^ Dfex)DK.A K8h dڐ֏a0mv 7_f"2ÿՋ.XR5%v{o^e#ױV4s&'F ]܆$E16]JB+ȫ]VvKz,I2bm5kDPve?gQP֎?@i)TC+HX[Aa M Z SS9{ Kaϖs4nM}U>ѹ'(MW"[k4CѝWJa\kȗgZhwLDO 9g0ӇŔ䂚˯3|Fe ]2Ʋ]YlN( H2rڄJɆ{f)XswFO[^Eb/R,ʊ>Po/Ođc:_l2NK*Bur&}tc*˅N=ñ(dSO55wMg-Xɼ7^ZXXc eܱ" G~R }kKp2hsQ0&/v"衼OfcQIIcdCKMY*-}LMh^-G_&s+]ter 5On\4M[].6*J7.ɱFf L~sYU-+Cq#ϻ\eꡑ/Ɔ)>p<۾pC_l~J荏W 5E=_J">嗙)SBih(yF(}E79ヲG9HK9? buS@DaW]ձ=Uqu4DS˂#+T596iEٶmkm۶͝mNmdTXɁk9a]AFn V_GfUh&}Z%>ź2كRr425@P$XpB؀Peœ\ YLJ!:wS;R~ўG~^o wm(sׁ$sz}\0' 4I䰧 XcI6]5Ӡ:i Z}uhA4a-(LFX ۯ IӬsfwpFEW,|.H+!a#Z '5.YuZRT UPY}|EJJ\ԤwOue"& )󮁪D%~m׊W"gg&9/I7`\DaSx BSS1AV_u=N%b(v;}ؤt<>;tx=YA#l4(G܉r 6~~R}UEut>{Iw 4Uu`ޥX K~}_NK(FDN{2 x#a+͆-Y$gGcbKPf$otB@@7QRoO?=٧l<:hMtB.¶owQh} ZObe>X^h_ދG& /ϰ8֟(YÜ%z*gPِPzw-'~(o.Y1.P+d( 4h Ɲ*J!=wzO8܂Woqm9%S֨_=%{%/3JT_לn=;qA593N)DoGlAZsYD&:>S:쿃Ooty&PtgKp?;;logyT8Ļbl!Aɂ)}4APږV~^,3Zr{kQt%Fڗh,Li6 @u Qu g&.Vz&Qܬ}7Od΁`d\p_#KqY-0}h+~aDA>Q1]*㟏,TSPD--NUπ[yЙBXFMn7pfaAP廐h׷m!~1Pu“O:E >ucvPD38'n%94yjIqrA9 ` i]W,I=n$RӨ,u z0;O#v7 PV FvOBi+1ڤ-_ȆB41cyRy!V8[)3 , Ԣ$ڡCOl[-u˨gBtMZ:2}]1+t|_t1Ny/gCW~dvXR86~J o~3Mf$^,%;"GN!#sf$)G8R]mW@F$A%$W`!t,Dj0Ye}6^jBnc kօ!x&]&LkOd0b~rJ[fJj]CZ)0tgR@F‚JW}|_B4I5<2KYW! Nn )SkOuvt{劯E2+j8nMsa=Iů8Őc?Hga`,=mTaq*ҍt hKYi*!v:pץPt6DB*i*(JXgCoedh&/q}XqjQxx(dV21D!j -J=&*iB{ ޴,oKU{4Ha:а[s%QyJ,B#~yq)hyd2/EJ5ZjnG-yc>cvZ;u0"cy܈Jͯ-}!Lb~og0w#*<Œ4]cir/ 丫@޾esojg^t|  6~  ʀq ^0g1-Ne&W~.tVcI>mh%MBT%5 e@ݺep ڽ*";FQH[|=,]RQ]~KSj\m mI!EEi "|,P"hǚMBI8SݮvKjlT^NU{-d'l]Z|·;)1*߅ՠMn״}J7X$NpV>cnhBDcIeIKY@n? $kц0,đ%(iY*A1oSHLS㳫M) #pn> 9*-g[#d $ {[|: oj21 P.?P-NKgyN!uEwpomV›V$t x˘MD=#'33]" ~qds1VYFka__?>_Q]$b;n(?#^&[4̐ifx|6vl$ j7&M:Ȼm2r\m"ґkVW QG \]v'F8{Zɲ]k#)B 3[O'>)([3[EL7KSo?6/tā1X0N$lm2X3q c SkKLO!/ i8Dn'QQXPHpz rϳ-- &t=&BbGԄ m7ª~##g.969\чB]=lsu';e3@D 0P=gcI/@m"+VK捾q`+xMEeRF^$%RƼ+G^=rCQ"B.ąxN~RzparA%[3{KL4R`d.d.y Ҁ|j&8.$r cx 19A3o1X:ka`$%{_}@5EPڄT,`clF9V3||U'Il,ݾw2wb< ںߗϋ^(o<>l˖wYY`4%jԩn\Vd? 6zoZ ҵ !Eim,m_}ǁU k %r0Λ(mӓԥ}zK+6/܃E#ؙs!;QOM1Vk@/>l!$eW\I,Pҫ"I_4(v5MN6_֬"+ K_ᅼ*e{ȝcǕ ts|ࡐCp51o[b|zb}֑-kHSf{zP]+8@Z8τ'-TT_&>8|/%V)(OS5vJw%ӄ`1;k,[y9sjڳE[vF V[3LZvXf!|1fOfXCc+.-PV0xཛྷn;yڹNu)[ӧOG$ATG3Vy|Ꚗ(ב"ԭ[-53.Ml둭lˑEbf~tɭTE*Nѵ[ܕEuGq@ (LIVN_1oc;aJZU3Z ^^㣤@RRsd/##DTYJp;xb{Q8C.,LGxf㋍{CIMXk݊]_FdO X;k;ox0Q;f,3:!V;Q9X@HF죵\t+px8FմQX>Zd7#߇%N ~ {؃s`~]Fbս>h!3 *-5=_ˊ`VDRMq4 Tyl1bVȉ@2t]svBg኎=;]_AmH[z,  #Ir .O]*W#J94Ju-5Z3&c-0SP3ՈLx@4ߋ٩I\war^yˑX/{fnҐz)^fh[0<18QXŶ>c b(G؁fS53Ga32{oJHaֲ#޼(r14w:J{3;* h~-K3Ɣ7?:?I"|L٤HƷAW}->G7FoB7z/ci^% o󻢑B60}yp=;äե'͌U 7Dhx|2c3:XXB\!y?}7NvtйԌdZLͣttE #`axeT];q5c\4!$+`)U0Qdђ RFf0Zfb)"!nB]^~fd d"x3Q7x|Gܟnd249 p,K:y~ǕBba?#g@=n-Ϸ[fzS f3~P6ҽy3UG T`dj\v z_DH@pVf^cz!9d"@fV{¤.ST8x[Kl:tVk>R:e_I=V@J 4/j߃-1pw`0?Ta>!\̍ogl!>Vx w7)(tNC}Ivڋ"Ç\-8>9̐@J70W?߅K^Riw¸l4>અmt΋,#?t\z &(:NA]OdtmS^]k .Q <puu) `,S (8E p81w!I]@XkMYIKR^E壾&?|/S Q02^l*uw#S֝E}ߓe|Ey,Pz+#NE^9))hw=  ks_:fqA>'bd6Ը}UY;H?pg8&QS)ֹtXȫw@ﱲT-wJb}~7BKRۋ*_ bkks%͗ dLP$ w;u5sm爵!**5`͂w~hu BjvPRp'[ULmٻsfN䗦 qXr F+j;ZE&4hҹIPyX+3nVΏ*jXi>DN?b?tS$'Iu(u_ů&+V;VПMZT͈mnPGCf>~#fOcw Q h4*~)&B"Vڒˡz̦O1b] " 2+6e :BP: .D˥:mU)~;$JkL+)OO$؄&RK9!?WQzó} _9> stream xڴuT۶>[[qECqwwmq{s>wFF\1 EezaPdDS2s+͜ , LLl@C' dP0vz}`bFHmJ;@dndPANFj-Ed`af'+=HE҆V WG+ @A r}ZA#)d PjTŕJ 4 @ETYEU &,"$UU<mUqWVTgf3h'q|g7wWS_ NNv< fΎN 3;[8\AVWamMd;]Zm$@+mK.w_bpos#i UTZ:m m @ rKiE]2kOoC1C[gG?m uptr;"`ja ϞY%WV}o<[z9{ulܜOXL`f07(杵#ŸY 6-PZؚ3PJEL=fl'_GG^oO;ma | x:N@o*!0sL,[}\.ek p-~g/4_J>& [kw QLp7R?5oCC k4/u CY8JXM-.r)'5o_"?#e޻ş @_4::8vW_06ىL,l,CCw^`agx27 f02؂]vNSŸ b0~#qs ع}y03M YM-f0}olo`GbĠv9rߗ<9&߻?'_XdT0y60oz (=yҳ11jٽC}o?'t#,/y-K|J)q4cӦ pŲwH~ <>֯- &_v }|PąGTJ;Iir5f[c['],oI:k9ЮsMXnKhKSNoX˟f rqFazt0ST6'(DekkǮuMҧ+tHP$ N^ncclޜR!e&g.LXh3%.JST0MH=#[# *)kTRZ~]9QR0ZrS!^ވ%)i]_A;*)pzķ0p̣g)lIX& 9^zAxŞiG|a4հY$y"-Wlx5#l䣚4 Q^}=aó{vvv?- T•6M8]BE q?8|~71jDS *[h֑Fn 3D?ovcc!:qGwp›jl7.3u_ Fj.$E=4;kے-IRH>.QKdd4t VQt g{Xlq|ЩNE21~+tWP4jJ.6)jBAhmߙ'4j$h^ ЊQdJb'@Â06$X芥ߍ8DuvyQb2Q~3TP,!pgnJMqpeJ<#ڨC 1)Ƣ3U R|J޽"$诜Zs1GqM[ˤ!Rl&؊TNKӘ.#p+^]`AIb.&mכ*S%t ]GP6|%S&jy/ߢfT)4Vjlzp)mӷӐ}

42c:pيjiBGq$6kiڷN56Fe-&h±x1`8ws CkHOamތ^~䬊ZG0q1`5(x>pojqfkkͰ${-CDm3WR­8v5[ixSb( )Pۧ8,T Cv 梘׹ȉW7d*0Ƌ-By?adl?b;U#/{ꅆל)^zM-Mӑ2.-bT9m[ gBH4? ][œX$QtiI?KhG =*ۗMcAe:%.oW# @|ڷySX"؜?dzA1vip+SrǢD%_QѺLw LzV +u߄bP&_=~WՈtD/t sv{r=Gw[͗nTxv1v%`[nLP_FcLc>@ϫIF^דH&>T=U,Z=m8#8Un#ޘr/>*4%i)|\Fs]=t?OŹ̴9*oN\l TOfGKrG\#,P_lX'W= àbջ+dbȝ䁙vG3x~%YbxI|X=@cy$ًc!06TOv$y0[e?F^D=ѤSUg&cO/Q.^}%1.!/2ZHJ#t4ht9}j.7Z0g%X@ӆRp%k2\Ϋ=T>iݥu2-=ϢӬ4G"gMy[kizҟ.[٩o+5[*L^.lY&X>F\R_g]뷘짍ű_ ȉ"KS,]`c(˥ yH473Cg;]ZHe(Cɺ贈EUY؛~OE;{^.gҠ1$x]f%{wbRTGw',\U|8QA=jUp-7m39Ζv6q;JB:w7*xz@*^\!m" fSLmfM ތ7wkkWrHMD2)dFQ#D*5ιݵ̨3) ʯiAFt_:͢8Ґy{Uۢ]7%cb]~4d舌EqU.9+QFH/^D/d!WLn!,o")4"ssuH(kk7+MhdhB_yT|)׋),|Tv.*e)e6(Ԡ `bV}*- -8AD7nLIjglu!ٓr:A|Ͽ_ P}{֌uLψT]&nGG=)gza[mK(a_>;Cx. (rM ?%58?=KJQYL{v޸q0i;]^S;ʀo#yn6*X{,_Ƚ~ jHӫudPfI~MEImC!w 2K^ zGVBRJ7?}@EʹjTF)z4 ҕkEVبt>HQ7΍m[ɡ'QsR0c{5ҳ*4X"}o՝.(H+M~W_ZwBQE*/ )9 +Żz`l\Ic&s>f(HXXBA"۱AI<:۹1]KaA9Kw`ye2kФTK]Ϝ}Nxñ&V7p3-X.iu=;YmKM5F"ڑEn(XB;v;U):i}&EܤU`3iErBƌIE{A&^~s*Aj^"G{ M*)= 3Q, %FalgRǔ/7ƃ(qt%gu W!tA.8g)"(2S8aiWn\SxkBrY!,koy%4WrvWv/ x3}ݧt%y`JiwQO[5AcI w8]~2/ dkٽ?ӯ0D)35QZ4%oW%% Zmd*ʒC55 8-<֛(A1QH52Bqy6LIKn9MA2ȾB43HXؑ>MB!ڶDv^hl 6傦sNQ'2D";xiuH6EcMUAd Eܒ/u3F?t?p}0Xkls ZYL1jxI.f/嗖 ѯ$8DJuO/ nIسGP_%&[5}[( f[+ !AUՄ)r!+'zOuz6ȵRhZKĂgJ4 kx^v*YRi\9^Ēɇ ;;@LẈTurɽ߶}&K\/33§v▚P#8FЋ9[es.9W9ӱOz;3rԾϴ?N6{?6RgC)"Ҕ-QktMRl1(bbUf<$l+lYʖ=nфfd2%.m.69TR>8n(ӨL l|7泧P[]ږWZP";NY^Uh"90<'Wn'PrD A1*P;5UBզ/Mn/B7.?Ǜ"z2{aLC,zmorO4Ȏ;Zu ɻ+nYX¹9i)'|:N)W7H|YAvcy6Ai86~ Ő`[h?~]FC1aZqwrsĿxNn~UkAEt0H' B(.:Ң *<~wWBQG:TlNm-m@uOMZYl[Y(diooj'}GmIz8xu 0pv3"KO`FpSK1}(x1Ζ<KO%뎨•jy˩s'%se( nm/|SxKMV0A͋wbH-8]Idae8x¡96G)ܠٜb sT唩 1Eofo&@qd-Qc4,҉'hy+Sш '؁TC[Nt᠁™ ތ WGsٽP.O nyA 8Bp#/'YB+YwWqVPIOmZyy*1oSFp?L5Ҕʉ{H|() \5 {V *zi2/ j}*ѶӜK /9yϞ$( [Gi2D@98c\jTo]>ʋ#'}[rM bdah1JzF2ag³y"mR3Մe[8 AYi=Z/ɟtP C_@Wi(uuEKL6C5ƫ:(rK@C,Ih~~-t+{*@?fYxE"6(έUbP Uƅ{!^P-LPAӟ9M!M>9e"r O~mu[xxPԓ8C^˙&H"M_h {P3 9?QTg:c#sfkl-6#o-PEWC>Q袎: laVicfdni)dž6>K53r.-i᧏9R+s>L\+YEJ06DsԓZGWG"d:*~p>{i jDZ&F6+5grhMecؑ74q{CZߧBs \eL K.|j%)]͓8C^I&kLlRjAy/)e탵JL‡s-h40.4K5S:ޡ3 Q“v1}u@oɷ F ~7O" `X"kJ .FMAO ;oa[)cA@(@B1S!qG|edy@Ge;G`Ԩ71^Pd0Nt.OIGJ[{~:pmJuq]-ǮEB<=R)kPD*{0pfT5`ѤNS3r.Gc{> ruO~^.άR K[uS*qmޒRMJƷ>B{7ɖأl/(aI1^xdZ>&1+ >)[9̭1b߶cs!< ?[Y7W(6cLR0[cK< Kz1/CU̇Ψ{G|򳘞 *SR9x6z}AjKTO=* WCX\y*K?n`bNҿhKo|8j}ᤩq~i(0bL+"2"g܅;܆LV=12p*7SE|ݯ"{wjyKvC9DD*DkΪpvA%e;wI1vVnxXdB!vi.ae6ObO3u=ѸuFg%uب,K4b֚{Fry8ƇۗeEvX0Nje~FȨ!y;IwE1Q,$?MWaZPBqaY)&AlVNo׏x"T~ +۫Xu .kU~*m]|{z:n9?N^DtQ,67HTcf]ݑ! \&p'cNuɁ; b ̘ u5:PL㾺(UKTӲ+$_8u;QVBKp?-a=DVy$sn/}h2a][SqD][қ61 AN=ɫ]0zV2&y´s.0caˤg)Z98S_9!%,_\S+yؠҾ>,=3hT9r8/":N q/[e&0;9-( vF:'nXnNc<+KR%EWD=3UXgà+?-YJȬYC|ţHT܅CEQW .qMo&c~ hv%I^4%Bg !J&2 ֣gY*듴s<ϪcˏgSdP&U 2ڶa0Ʊh5so"?49 N =d߾>T|W`ɩ ". p697)}\ơR:͢IwuD2l9eYEp0^Flk=TQP!0Sgz7눁A&)䃕a+Dmذꎑ;Þ3_9QLV'zZ0^xg׵2H6 Wd6)QC1_j<~o2k;Y|\<崊IYf* fo'E-.dC&E* ~ڤº^]mw홁_c.^ - %`bP&cb,G?JmF27 =hap.Hv rYö ^"P|%cCNBW)]{B>)xt-9(-3 I ,#AOkN%cIy5/ZӸmqk~lJ|8op̑ iܺ][P_8_L,܍sB;hdi*C[N)=܏r{ AϒHn#ydBPWdݢ=FW[XVh]b :܈o"Wʨ_z 83oJ&x? d8.;,u0+/)?Lvo}0wm XD o<0+@NF$S$W~ӷ)N[6 jXlgʸ3fҗ,\(jF&&j!Z֝?a[īB=GN2 "DjYP-&gLM{x !dCaZ*k10c_û <1dMB!^a|ʏgQP=aqҾm uWnM2ΞY L|)1PD.p6*UpX"pفM<59G͌K.)E6ULn+%}·G8ZMQ-J"S\kz½@&!Ga`OI9\,&iI!#2>0ݸaX\`X֥>%h3Ƿ'ʬʱ#7vr:U~J[˱GeJ{߂{ƪCinUF7 Gzun8S;G;`͡n%?@s("%4:_/\HJG?z=#yeSz@=R%M`>wϩNȣ0bSf?r{ЇEPApD5'8:Q|w/9J*CAf~sNqdCzlÒ߷#ľegRWY&IkUmd-Ik\U7ORCulIoõc%t7Ӏ uy2M KC囯ǪƄv*~$\>SeB/:oZ? O(Ia0DWOkM TU3Kp݂Oa3r~A"0q+,v+N_t9x{|=q1z9C=q  YT,J`f"-˗o\}7).$I]X VR1-MZU5E5x^y٤0ngO {62\ul=ӛjdif:Dn TH ēKkQ}Avi<Q)+8o* }XO\m2%)ypӋҒϘb saXw/Mbq8]w.AB  гu1л4$W+tƉ*e  뚐.BA։.WLJjlh6GtL2.W^MCI{%# ENyڝ^W, tҾwϒ2$|'¹uFr7i#Rќ{l]8m32gC4ʐz'Ue?t49ײlraG8uP]*˗w`cZrW֊ےrC, f eVSNW䟩5ŠyXQ7I ˢ5_Q=l(3[B0|5Ѡ>EtvEMO]5)Pv6V <6.2RH\fA"tTOvĠIv1lx|h`gS}ڱP՟1g@P{X *UT;hW<;ѫE&\cpٸb`U7 wX h4k|IMyIZZ7c!-$2EsSxhp0;'-9 /l.;>.ۦ?TVX?GYWJ!hxB!hez5…CN{_ 9N6ۺBTzf2Hf &s6t@-A"i Mr@$J!&D~OMZGv*fO$1hCx% ;p4ٲLW+ Ԥ{ᩌCqi[ ۞nea|Gf-.( %i=;RiVvJ؝[DM@5_ڸs)RAEx o|4C.gd;^|SKtL*4>:vPֶFuqϼ+Wzv`)"fA;8<+yYXJUù"|-I3㶽re}ZzŗKRlm snB[a]֢ω2Amg8*B0kgdYY'I0Nj\gd(5iZqq#Z,8 2d1Z/i} cىj%=B/Eo4ΗI7sA}iy&K[&C7A#όg.v>6* k۰(~bFh2~ѿڌvZf߾^=q1&9&R@5riV!94&!I^|ќQ٨*;H, lJqF% 5=֌]Lr| iZViYpdS@e01v-ո'kmɞ7q;뇸"GNOx?6}Kw"w^?vl|9<nk!7t:KS 'a³t$7/%lRb5( -2؞i)E;4O˦3\1{wYLfGJf˚/$MN"[Ef y?w/-.#3[3.]%tɖ?\ ߇ZNVjNB<͕v VouwVE(`DJOe-?RY#w7sX۱K`LQl~4=M/7 0=>n~t|t1+yY<Vmu\Znt^g0R:yJZ'Ol=.;x*$ȘV2E*VkryȋX%R|KB?m_sD)"1@]bScܱك~^)vц't) ]4H!/)GGsS)E#}C=d`. KntrSіI:6iҹcHE5V+W=/?>As<75/)UQ\^U'h\!ݰdr\bzP,?@o8[qTh!ORXਰW, dáJ pH45)Յ- l>.iUˠvȑ±P<Nm1JJ>=*M*kNGĀ7l2b939H$>+`|k$C=hf~L~![s`_R})ngA: z?.sio~橦I2@Ԉ^;?Zy_\9 (ZU<pہ3tؗm2-5hLS4rG#P"0_\{jYn5>'1Q,CZu@=U5XW)jS恝@^9oT9Ds-DMKq_EA$8.lr}P#.)Y<ָ'?ⴚk%uDG6j&S=)-%[Xm״]$UmO8pzSr6b~+PVBhb.%e8~=Mo&seEpx`18^ O1PZӌ$|!?֒?eښ>s:sV iXpXwY#d${͢suDrwLc)YH{ËKj)m: IN'mSD[^KOr#J5[HI- C-Jxq/F`}2M% $h B5X)q)3{7nӷFgmڞ"e >25?b||F :db)''6WL<;AukEeAx4ybW$y!:lLC:Edy!|ż vt GnM? 7V,8trCKt+51񟯱XX4=d]v:u*J0\6aP߬2u<%/ܣ#4v;ᏬWj6aڑCFm.60;m 8H=LaZ!@/ 3emM/8:y.EX8b~׹Ϥ_H_'R`,(&&; @C"Sp(yӰE> #TiSPz8C3F. %a}9E;"e"8Wԗ '+18&; #qQ}cFZmoOژG7oB+<2:re XINHt` RniaH}w4_Yir·akzX?̓XKmK#gtBUTwQL:7O>Ь{~.g[ #6PBHy<FSe@`Ä0jA5tk [HzsMYl(?7pCyJʾ?fIOaB::QĄ8x[]vExaKu%"Fh?ҁ ]lvTJGd=$P00X u-L nVO1ْSѭp/2L:pD8(Uy=#N!n tj6C g6m3*箖wh ! Y|+BXĝ$f+=wh;v-3j2W@mJ0ߜ-RͶ ~縧?16\\:z8$qʇ*},:4}`Cto _ЩwqsEJ0;$OK N6=[[eʷU>Z*nZ o)f{H73]D˚\."wۓޫn; 0<^~bw-! :)Ua<7Ms&zUgfmC&O :OJ vM=.2O4#LtY!e2# f޺^v4:U7Ɂ#((H9tu=揰oXV3r r|4kΡ73{0rĊHlj(9EMʕ,4,mDOTo <8(Uwd/pew`VۨwʛG@\*蘘ƅho$e2# fV3Fsb9Ƈ`{!ihrq+.=|'5q&;|<ȬHzKMx6~; dY93}xmϮr)=3'[N GGwM%1ZEQq6q318O&+*n: CR]@%7cJ>ga;g}{\)RA/-DE B=ݑZ($bjSvv-_DG zgVDM0-5$wT_csHIk/yGꪵ[EGp[Bks揍Ľ3?UWQG潋D~P@DK ~'y/NwEhU/ۑvxA .<Cr"qUdd!UD!Vϱ]Rha݉r鉸 eenp"bt s1G;78\TUkhA%Glf5> ,w^N'_.߫ȑ. 漞eUU?%y\ Jf0CVf@%\,ck{XA{ Ӯn&%Mu]z?=Rت5]I| W^I1s~VfX +RC6w7B6kT,IcCfͿ<EGC 6mW#[Y?Voe>򒊲u|5rLdK.=e=:jY,Lڪ6S2 rj&ƥ`seg(E@:M̉-( vD+XP (s}y ٨H^  +P=1`yd1{wܲwI ESFW6.?B-S~ 7 ROvekP\,*PWwm6?h, ez'5b򄖮,m*4 /Y7mĊ'k}P/Zgvv\n n!%GXxGCL.vQ$DDLXZ^Fښ$,wWdl;3!!nfI@l"@bN벜BW/rzú WdRw.$5F4n aqwXѓ|8 ^ p xnOnpcoXݱ]eQs<3mzom@a1Yu-bL_5A)ҞD !L2m0etz|Izgl|QmYz/P? =jB{?|W8NoQF$Ġy:qOy{&gvߍ<. խ\ cVO%ۮ~aKbF,Zjbo(cslN@,)^ր* .xEb7<9XHy wݮr* 1u+с0Vn%Y4򱺪ΦHAm7+Q0ӷO?uFJz%&+Y,u?:/^3[KOnd^rhx KbM]Ms 6X(D4o,l~aٓ?0_ ZMxeP$v.h m ]-*ҲJvH KyKTn$؇ iWY0CW&eۨ-O}qkq)aKPh8,vdD"7bF~k2bN-cAAQdghmy{k9Cg| y#M9kFq]߷llZAM#> BF.ľEN*QUO-}FQ1ֆ_&ݏ8NxZpvN9Zf5}pa۪T%=C#/~@6!~O  IUê71/OMsMu_ʏ5rƷClD\p!s*Eh>=A@g2z9t;cEɖ7(&tςuF/3Dϭԫ"$؃f"rզ 7zxwqN)=iFd`痢#< djp/|B@"<HÂ`1ɳ@1)pUM0l|#d=nU@ lVÜrĄN9aQ+fZ%!*6p7 9ּbAB_+֟HI NU@ruX+.6|#X)Fv*Q37J#0d-`wt|D-Hij0+~v~ާB[~iS2I^7ym1#jOYkLXr9/qUn8 cg s'{2On8^Q&|!5bc>7]q=m)|tbMdX9?ÊQ^c;8:pc}񩁇j\yx#]To{Lȅ~"C R/7w;r{poݍ6(cpRK(ͳoDNxF|VKPt[`ă%YŒW0}tmdnKxM_CY{( 8a1&* ʻoH] x*\13([jѤf|ɡҙ{Fo-9o؄=O>$b~( shgS.hHo~עLX Ă iƛodPlU,Y):}JZцȷ'E~:T=PS2DBt8+"UBS"XuHJ0{NA1^0ת yި c򏨱#tZb:S`Un&EpUz4vN5΄iйxI4qHUđ qxUjR|]?o`x x VT yg4TelL/I_i1 endstream endobj 170 0 obj << /Length1 1712 /Length2 21170 /Length3 0 /Length 22262 /Filter /FlateDecode >> stream xڴcxdݶ6cum۶I:Tlc۶mض:g}V{pycEA bjk 9102lAN, ֦VFffv 1-H `fdafdefAHA@?)t2Rwm sK揋O 6z2dLl],F S,#@h Ff[3:P&RUPVaXjSSא(K) 5@1G7ÿ ":,L`-(T9@mdhh`hgO}W[+OcA@_@ruO+8Oa7@Jca<L:99; |T*svpCU'.jgez֞FcF gG^-_3Kk%LADQFRBMA@ btrso<qy^7; S1[?U;"m>9:3'@ 6(#_ ́Nft3`Yigk03vz[\<\'g+04qC?Ot-_J[_g@iL-` 4C`RuC?rI:[[+ifdcia[,Q hldbO8gK49w,XNfGӄOV `RTQUOc%25X98FFXdCgS?401l윝fdGbaf0`7do"'D.?^ߑ:՜lZNn``X>O%97oQQ[7Ovv37xɿNxg?[; hdk-{Dl94E%ljl'>x)Pп/V^W'TMeՖX5so"o,!2ɨWMJs*S>NИ8yb@LW޾Zm e 3&Ҩ_dv0;,]_.]JNJSaY.J큹^XϞ5%yCcGw}HɯWH%*RɧjG+}JNmW*ztv_%;ͣQZ9-"tS1kuMZZ5Ȇ}]A=VЎ_}^S_ha9r3JIꟙ;XmcLᥐP~Au~"S^ +Xvok zқ&V lӋBm] PUD%qDc[wilV~g%m ͈4o70̣3~*.їM# kq,נ&Qܠ,+ȵQ\NU k ʼA^J$h "4ڐѸz@"XW|rj" ]oqCr| RB(Os@U|LNC `Іٜ_#X=FYpǙvp>`;xZ YO&gMU-Ⱥ,N/b̺iO)_D>.O{-Nc&ULKhBXnLa>^,pxnC.V 2u]ACB`oMW jU6Q!`[S8=N}*"dQuaB%o!spżE jWG1m| *MXQ G)q[|5jSر:ǧ17 #/}ΥEY,j|I^%!"~8ܓ`}d!0N~)Zjzk{"%.?w'GH\Esc!<.Vs2hb9M1[m&4G}0Ѡ*F7ӟ$U#V8SLv:\4B&LLOQ*ǒG:*ǔ9Z%rG}#J hSy=7*78k!e]7 Jgw# Cr9MXST lڇM$R8l ftXz3v2;<=hbfo&"w2d! 1Eu{a`@'5,M6_x$ثi}J5niu2=yJp1ݍ*iTSd>V1uT}jָtS\yN_,\{BkDJF@u){QEA\MW@Ken,8ܧW7-ߦlF~ {&EsdP- To=6מĹs>TG]nskcff3=?}|"l@sʦ%̥SG+]b(;l׹sBQE关FXup]JԔr]+ۀ/L\^IU¢3#$79~W]א-ILc(Sgg5<ɑY#{R?i $MkI^JQ\9w7"h( Ғb6@ȟŝ$0kITcm: ﹵|Oӽ<_W׊AcW; 1nƕu[.դk-ML}+ŶߪUto=V`_m2T_BpM*?0>BYKr#~YtcC GQ7#f)ԯ,eM,νh3wMyA(ԴDk^0q][%϶[Auݑ,8s^顖X"IcǸDY}{P%?K7&QzsnZ\Ǘ:zo^Lb!?E`au)ӧ'$g4V"vEr&7/Կ2 A`auߎ|ݒ|6{LN4Czb]d񕃅|یdRDf,:P4MBݦ(.upͬo_l4_^V +5^ge<)3s{m?W/68BVD{rNC) h6@B @{z0"+0wGIvʠ /HG^r^A)9} =Ï 赩:-r^lom6}YE({e&E`cT78aKS*Ss :dLߪ4 m\|]y1I$ V=ֶ0K==-**QS?!4XiiJQǂf.G6B]{SY(duR_(B/HSGCΊ҉p#b\'SÁJm90_%7ҤlԠsoەbC^ r/[ȢO3+u 0 BG0R'(p(P'N1׺N;׍yތ"T% F4}ɄN7T`-IcF\t}Bp2}44ENPz4ĸVD)}ĝu&Wv/*a#Vq,@KZ K~~]j(Czg*yc8շ9' P3p5kg39D*GO5"C](0 =}^b;t7W^# s1yG <"ǝ 3M@dԶl.rZ~}drf!?/"\ܿ2eâ-(ڶ_ƷADlCr`EoR|( =Sۯ^&fLDbbI#jcZcY9e^՞Ԇ1c{CtR Ǎ3Tky(g ~J1sd9&6ݯ) mV(N%RpJZJ q( Jғ/kM{YU5LJ.TkժnV338!MF Ԭ=~'nWzG p9Z]^CR7dlp>ÇȟhxMCj ` ,iA܀f r@Crv*O5(49.BKި{(®c^g9|1sq*.5y " A_c ·@~WK G7ObTiS[h*?z%78f,G'"ހ.1SNuR ~ޜ -@YʹlVj~ݾ~W N:rB0|EJBHbG5=dEM_w*Wʖ>I#xu\FZfQ[n-XBAw^zk`f8_]&S~'< ]>z˂]],qBhZ@Y1 }.m 7IhpLIx:˙3\EԷۚ{6SØ'/ Qs`mG^Xcحq7#n3<䆳 1qFZ'ާL N8D˽ Wj Pk=ZR-i4j@".ؾMw}Aih%8HVz(1c!@O%w" fJ?\ecy bTinEI;-K}'fK6[U0P&~ UBt/c6lv9ᑜzu01f*~@tt0 ]pe_0="cJ{*2;k s~|v|:7k؅nyu뻜1Zvy\ D~:#-{mnI3@1"tVi3 z<9bOPIYtHfM#Cuo9 4AJ؀DQ24=? !f9b=,k '56 ]sH` D` xI=XaolJ#>};!b0e6(}W=$GK5x퐟֯_Sc7:3k׭}=B7oEqt+6VPV< T+8 "q} v+TbX˞kשK>sAX8溟+!;lZSs-NpWSsMT90) pZɴD%Zj3^U/4 eRIڳVgӃonڔ l(Z(TՖEƞ$w80jdk@A8D l˅*l}ݒg)`ODh绿QUyT'L2EQTyXZmQ |6L5 XN 7S}ĝꘔE2V@%gnR3o 8?鲸a~ݼE 9y n"_#R}!…xZݛTǚ-JgږH%O;~#ݔtܭ9Ñ`7)œ"Q}Xw>#t(' MP$c]%˴>k)Z4²Һ3LGvVK?nh+I)ΡtzHq԰jKOhsb'6ޠynt,ڳ ɹ :&ko -3,:~k-j*,&J Dp>IJ c 1  |p1Li BD8R:ʌfHp օN/80}e|E^m1Yv 4q+xc^A7h0j.`4ɍBdP C;1RsXSY^Z)1[m}OZ,nrW?<3=K?V6nPy ϛ9_O}!'iC%wZ^.FxDV0D/ -nv֣YQu:!};NWo2s6`8n}N9di)Z5}bcM/9.~4gf,=A$*gF> C j0ҥE Bu9هxثj$ vBA^gUCB잲ܪ Wqם VU4K4C2"Oq6"M~pVJ-cQ1Pk n}i4fc2kE$~@BdV{_f6 g\f .sfç%I̷3ynI؆\B){TFu{q3aX ]V[}ܵqoY}r6fP P wk\(ܩ@GBN,N?!E9LyXŽ[ ~6BLcON[eWIo ᗬbŪ! >&B7YIG!?H^螨%R+=E-XٰwVd줻3ʋ"+Vip<E2 ߟ!9}:1=o`wG$oF>bRa&4?ϳ|]6*8*(< l8z+lC-mN C~WxnR#~,}L8j{E; @o~z2æ,)/4=~>B3 -wϚ/ 'CQlm ˢQ@n^Ay([7;]N3 eTApjs7ap nro! ?.eAͲZ{R*Uwx*__Re|z׋6e7Cfw0Y3~SQ5%)ǼDV{CH}$W$?C;[PfYuT=Y @4_ujx8;! #GCY;gH4vUj:>0|I1lsؿ1ާyX%7IL8- l\&l%V6PE$*31(a{=HO[v׸tJFE`wЄ8w/>EJ~,ؘ G{UϬ{ѣ%;z>ޅTm)%<|"[4TxKKK#VfRh6gSH"b9Q N-Fvœ_RJO>;cuRPdƄ8ո^!`D8r5f2-vvBY8$h_E`rNlìdj!E2Mè""n_0{/c Fyf+G lq9^wfݪ5ĻX6!7; X&z=WE^/C$SoT4L:'&>c%Ltl 1V֎"WWѸ6<kk^ Z>(f\N2) knlGܖ V ~N6g^ph-@4J::HJ  l뚵VVSlZZW֣2inUɾ!/0G#Kl!`0q)1љ> A})w" O9KV69wznыsKn72M͋~[١6Lϲ^p+|<]30g'EiD۾Tl5WN&Yׅ$N/VM%٧1иgkCڝr)Yb.@h5B rX/˥Ѫ.x-&>]1ӇGnXY";{&Ɗ*3gse WDSZ5N̘bMe؈t]-.!"3ҡȸ4٣IM(cH3^Z,p,gr=m|a3}wN+=E{o n=-7ޟiqFqs*I&[a~=+]z&il(9]{կ{y[gs82s"(,kf/( N\L¾uyH&szŕ꒴rcN1dUK^,G(>˫EI^jra xO&Sƴ-2(ddtsI]V*@x}Xٱ ) ?U2י JUCž~hsM݇oV^!3h)t5%`W 0 ѤXu`8M͸m}w2ծh|9F66?Y1#܄c ɿO5ȖڊPyfoͰmK7{lWI֟ †9?*l'Oa6IJ;&wR$oIhՕ 4VӸ|/E-Vӟ>l<6קJ?( cONEKSX(2do<v925'=מfۈ7ty O"mcKC"Tzr37Y!ZgE Oq'۱H(B:JF=&IS^/ xiQEBKc࡭cEʊzp]P^".<2 f*wGA]?)V[^λgpE_l"A[bt\jl0 EJwnk&X% ZV"9.$:O=vCokp.9myZ=XWH]HgP-TzD皴8r0l.mNc/y jO$q|'X/Ʋ~؄e=Ô [M9 yXRÃYce\mVWH( l0w8581jGJGm[्Y_o#X.6pBm OȰ_C + Q5^Xq$-s0龁#5៣֑ z;Jg Z&d*9Yy6R_DR?3ܽ,u{/ l"H&y͔8ץP*?7 9-FIxnFSL, 9hV젒]t⡟.RuӬG`L$ͭT@w]K({7nĔZ6wf@B W\apԝʛObMye[%Y]^Bž6(Q])G#MqLCPEwrQ+ jl(65kar]31d]T^ÂqU o)= 1+~o'0O[ øG~]3Cn M`Bpˮ3u%ġGpa>g>eZzF+"{lO u"[`+ NUrz(,N7jB~P #C:BsM4Du[20Xnr AruU"guv%RgǴϥ .6;MLZF}>1ە:J_X 8AOh=hxői2 F؜J@shcRo=8l 4(_.±$-N rl "w\كYLjYHDڭ2e} JZv iQ`/HD8gXI0k54/~B pxBgCZRJ "A4Ͻҩּ݁'9u^p^tцRfv6d3I6AҼxx8"aD9+KMox4'-8mtج_;Eӑbj3oZ7GdW.:)bmr_߽1[底 }u:7HFE51d5?X`7βsl(5lR6TWזMg-عR5a#&p8K.;DOmAbt!A $aDC/ٌ.;zV1wyVqaax#@q.=!(ρz8͗9'=`OrT-l0y!.pet-n!w)6RCSa zNrf?D)>z&d7f]:_|h?.d5o>@ `mD`r坢UE9qiUT1i2*L31.si' !xɾUޟpΉxxјlb|ȫgO6EzWmdjeIAlJV;FZ!\ZLvP_rROEmBӤAb^@T@(Թe.fS+A=8qKphe3l-*]&6piiSy73b%B$tY8ӔG= a2G{^ns5 o".)K; 2^/-5 *᳾2""oM6aFPߛˆx]eGĿpb&pG ;.)(ulJ-dLo{b%j8(D`d9NW UbDMt|? )P6;RF̗ [M/cAL^v+K/}Mf Q[2'% Qm?豻J""c#t:"g @*M=4JDw:,?{YfFK_!E/1*OL \:qXz xnk{dLx󋵗3ܟT5+ G̔RB,'Af|g8Gm5.:ڲ/@F>hqJe;&2;tdt0"4ֱEʓM- f$%a;0anĥy:*/_˴IPd){p43sp)JZժr&}F+廻&WM?%Z>JuA]f1|,-: 3 ֯A,3LH"8cy!XxǼ-g2!qC]V|ZW^BHCj!eK49#"y+oY>" E# !Eɱk^0bC"\9K:(PeܯB1^6XH C\y<"#;]$0A?SpEzo%l;F)|" 5DfRk%pJ܋"j#bNVq9]3[ua@㚶v9q5|r1CVbLsk)y$b Lt )%YQI05%\,6U@Su U5≉SjB4%x {SZK4AR0?(TO`{%ғG򣧍7qO'RIY7P D wɏ3bgWAӏ-m m' .5Mb\<6T1cLJ'{#h8?DϏRDtW<8희Ə }K/yVrH99ڸRG&Yu}kfk0&H̞n$]}=]O~3pʊxwNs"njlZaPz)V"s/+t!٤;pFji:. M#޻G{H]wJ(m:jpk⦥IwHPJ}?nChXQrIue6RY̊4rQpO#h'uY9<-r&i~ɰ Y9yBn2X{" M./u˰\+Xw acAW2:^V:)~aK"pVYD>-}!<ƚ]0tEն}X10rrreqluI睝_+u>):pG(D> I4atp3|d狿m|޸7m̞?RQT P3d#svO*33N1Dq%d?X;:쪴wF-YɢW#X5 do˃_Oz *ο ~;}moMqF^auR;k ^dJ!#i5/+=*7u\u#ƥ$'0洛齺5Ҟ`%v!~Dۯd8a;f9czXNDPie[j؇ӛ̥4^XMPs׾_j m{a:lVY[lwe8gS1״Gt.]&ɵ6I;;?K( . C}k>;9oVЖXۧ -_KΩ }Dbq܀eKg QF30pDWwFձ*A^g(Pv%f&)WQZFV1{uyk\H~- 1G 0kk/NG}>UlI!̄MGQnn,4 PcBQO\ZNǼ$)ze([&1Jq|LP7hN@sj>]5MU~z j+*wa(XӵEG /dVPPEMv? M4m6fvlﻴ+nyj͉ǭ3c">9FG6`E"[0v튌bc49eHYX+]"b'/e9,Ⱥ* Lʨ)W?c2 eH̭ J*C C[¤ m?c8m81=}>=LiNLϴ1qbMw:1zTR+b!Vdfe\i %bta96liQXa[OR\:`Uqs0c~bkDO;qθN=s06,ҽ+1M_v$vqS7)PȽhc+[gzfIaqSZ3jѐ)%=:#o:(k`?1yڞEWRDOE;:+UfSvbtC72P7әy1,0$ip`Kp3`s fTu!_g5h( xQQ@ecLrW3~~7i9x< Ϙo_Ig•;8C/TJk"+/  g 5Id|M˸$x䀇ҞUĽ Y(yŰލӠU@v1W}ܿ>6*Sec7%'zܚY@㛜G増oG[ I3}I.}kN4zC>^SXp=e`IhN4ҍyQmsыUd:?݇v) h\6Sw 5HE'NBLq#TJ(/C\ S׃s6|H곲`*X26ZC#V) e. ~(wΩI[,cS"*J vKܭ4O -s·a(M@rWt]wɫ~{sLnt!#fS r Vd`J*JF7c/lƭxnXj dOhQJq e"9TM'Hs:m"]{+쒎ĪrJ# XԶd #uS;&; tcw~Z^c{UW* mcg-sZSdDi x75Jy؍!'i0Np#VW>X'~9.8}k||Ŵicu7xClI>L*}E` BjHj޵>29,jmk<%c1}kkV0X9-`LÄ?U”EdכaC=^)-޲_; ~\s(/ʒ7zߎ< uUI 9  eԯX_VdpQcB~U\Q3Wؕdf^0A:ǏbΥ{+AB7 (]qo>`{4rj֮O'jWr${# >whbBP,{=C]pk) ,8z~k 29%*Rb* Nf:~Xi=l [|+ 6~k7 K̯0CJ:3cFWnnG>AeZ;۪wef VGYb -wGƸmKר uMs&}Y^P{YHG<﹩Y-8TpIНYtlXW.fZoH->3iܙ`*qt'7"Gj(=KY-_=Qۛ'bi(˂=h.ԚGm p Ojf;R!܎AHFLMZd@Xrl!op.XU81_[C_ Y2 LQ`a8hIjJUjDzZ5*]hvKwɞ"`G)"We2,[ *O%ӻuF4Yǐ0N3Ieș\0ث;*+rLj"0F!`Jc~$70Q% lm)J Wbj;I"[H}d<攗07%D)Rp!;*EC8GõT-FAZã!$W[5u_&[£¦CwhEԞfR2ş saܦ&?%O_yef4CpyHi`^9n(z^\Y4Yx4# QR;/u!%ښb(@Dk󁼯]qp7Vllѫ6l$e{ F>I-`~Er5DQ{#m1Ꮤ1Rx:P!&[<.[Mg;6DUl~wث<&Zy&N "&|q;a-6ܲCV#`Fhfoq^ОJ07a3(:T2/˼΂7=auqZ+@`S-cXuTF]a͋h rǐQ&b%p}!*ORҗF¤1a/ExVy/u8ɿK;-!ݜge~qSF ȃA9$_U~2 6屰22i\Oq\? RM06ZDݧ3n_Pc|2 MF{z*"|I'[v?1{dK>vβ\(^˗C}Ė#}J6Z!y;5!֧/ƝC tq _c.2K{ʍp G ?2 }KbmOsZ |wb g/8zj M K#bJ8Q/r^IC]m[&{ cRgo7t`V䏄s"/r߾f!`Q%:dfp`Y["Vn1խXSZ+!:X AER]ƌx,[[\>WRsJZҜDoz׹"䒲UD46ĚS7-K&yzIFeyoA۵4bd|XR'Ѻ3jGy{2Y嶯A8Hi7/MiF߻TZ8U7g]Zyz_|zprMTTV^&1G,kCeWk}bq Q] Pw1>#[Qyhnj_lGJ~ 8wu63%oP:c­+Jg4w+ ,í1oxb="%VHOl 3-rUxc"& ̿:P?GueUeB8ާ/#L #+$͂ώVV"'x.rMx>^}9&lִ)ѓmaxkoc ~D3ݱ,LvC…qMh(L2!)꼀~uFŌ>ͧ][JUPD_[g)%9KLʵף (O+Gw'RInkiYL* R)J3K6jCCIv,R˥w#jB݄G֐~ý7ɬc7ҭMWxk"5\7dR h\R->*k3tشl?]nD'deZ۠i 5|5o3+:S>d0ly?AF endstream endobj 172 0 obj << /Length1 2890 /Length2 29941 /Length3 0 /Length 31597 /Filter /FlateDecode >> stream xڴeT\]5%HZ;C=Kp ]V~ (ҹ^ÀHQNd 913rd@v &F:e#Lhl 5rr8- &Tp##@ht;MƟr@g#O@&?@Lglv-T'GKs 5X~W-L629Y[Lryh F6f@ PSSVH(+)Pу ۃ-@TH^U TH 7ȫˉ j)11^ ttP9lipvf`pss7wqr9O r?6q3lWߛ49'K N۝C ,6 8\YEEY3l0R  ܿ]io tm[,`VAXB!^/L?gς\l?4&,&&=m,+Y{[Y쁎\ ef y`m\Z XUn3+lk'S7<8OS07'#'J!^# W˟!]t v]?{ t~*~eׯ#ai ~+D|uozՀW0ݓ, X='RM" @w Ȅ'*KHXd2.JL~Mx\l-b@_y!HV['߮X,e5jTIpGIL{:Z@܂_y1աtNV tF[|@W_RuV޸05;ڠ/t.LvB:B2Za]}Wm|UͣnϴˊaumVLޢl3"tR HKICمjharB&%&e|ß*H"_NulizYt'yC}УQl˚>ܫ5+ʫU0'F34NHI_P*Wŧ 2B#<,{/ pG툨8HF9FZcqDгɄ_W|*Tp7]516ZOsWQ+d!@c!SJ H\*&_p-?ydcaVOxH7;J4>>Viʿ[g˹rC6N(DŹc jߚ"dBs-UBĀi*r\TY K/\?) =]f,ܸG+ӊ*ӫk@$W6b.ȝ/w"5~0[MI<3w;fNS5'?6s4z/5>,f=ЇJj0'XHbmwy*]4o!AP}%oµ)pPrPtd:6(yer4VE(-NK@UDGac"Gxa*bFsA%ˤ8?jzy0: QSI"&ky?gt΁PӅ=-|[*?@oT8YU |+(~b׳=50X<|ǖm`ùEsk%a>G o.ތNhqo[mij[* z?.u1-,lNpnԒHe%Csmsq?Z-䪎Y;+]9W>}/"z&p*|5z-MֳSzE'K6_^d;AabLR>c3܍E0N)]e.C\%0Qb:Dɤصb"6\9劕(_WڃLzgEmH ##xw欆\0mӜ9x<4PkFGg,Lz ,ΰ1Р/:"OjqCEWKܭyQXZ0s!Fǹz&=!e{:CzJ-T1b5]]twY4R>V?1I!Л+g4y6uR01j1 V' +-FL 5l-LiVX׻ۑ3NBpiKVϦq6.W=Zz V;=>9-#r[R R ρU1)-BP ;X|-ÚIA/5q~Z&)iڐϦVyI3$Dn~/ 0== uj2=;#BbrЏWUzʶMn} .bGZJ3/lnKĄyik×cNR,2}7I@JſrPS 'LПxv-A)V#AXKof2򼫴Ҋn,b޶%̰KgٺHӶ+\걯{i6 WE=mAͼtBu %3X\ *"H=ȶڳΆ}lCKW]\Nkԕ47c\`D< ǻ::hًfkjpmt ygW#?lz .΍sTӷ) B۴eKApiP%W5s,!68'9V\7 Z`3%=K06IQvkȧT?I }ߚ_ejkݮ taၙ鬗.$Б^?Vd0nt~UP

⤁4OMF%;PėF?9Su(g̄ ZQG "ej 0%KyC~Tt+yr;T OƩ Q3ҏ`wfQS&?'NW)$[NA~x㻎,WjW;P9qq@"'V^5žBF6$:2Ho_8tcv4爠4.+_9#U)<SpVzYp?Q8r;"^-}aC{ pA.nsZ.tc[ΞnǏ6•BM_֡}샺J4 fF\C$LIziQ_l X_>8 g>r/06JrDr\k$n<["dK< qH [=qj}tL رl61vS;LKѓl! dKG g r%aE` NtpBboFXBѨX<ǝ0heU+P `F[qAu7g0"]ȢKO'Vĺʷ$'394LL7Q[=B zc;#(K:leگ|W HލۨkYv,n8c!Yf}^Rޠ5 ôŅp7zw=W4%cp(!Z7زWΠu_j4{ևF=翩|uKq6,3qѩHLD1"WGg3KPEêLfj"¥o[~t-'ؠ|s-`z(NX"VV~a }Yw)F^<}A)/s{Sv/:i/n;h"'^Eyl[#оRB;d?w=6 V ߵ}:E5 9u&rUyy}vf<xf=AVJPdOY!<+@31d%O,E_6]-2YL g=>N4xԔ}ׁۨJ<׉|BP^:NIecl} Ovzt}__0TM(IoQJM1AJ~tD4?nu1*1<yJ3M"!}6PYdC|j&+^O0%0qgNQߋn>x`>Pd3xo_W*#ѣH_S' 76[qR&-l-"n Ԋ9a_+k]&?+NNcjZ+ )Y9xpNd}w\ 6R& 7Z<+!g;<5mKlׯTaС;d]?Bg?m3A›tbA5VmPpON6ҜCfTS^LjtKޢW5Iql?~8 ~orOx!-4.!6\i_R^>c:HE.IWruE'ᇕHa qfcBCKO&QQ,N*$:2Պ8 d+ ^s4uRɩ MߺaN.>·?߉k|G)J[4sR@bo\pa0@Y_ͱTU؋v}_A~O)fRkszڂ@psXtqsgٖ$Bvs[/Ş9olG뒦^cIބ6/^ܺ17㖔6|L#:3 B a:yxq`1+TB ۸k afՐGk`tH@?$ Iƙ*VهSTJyj44ʶ\u7E1jxwB%T'/^E^ܧQ2NN'q׸ J*HIuqu}E-Wur@^wQҧ9ttd<\o69GRCoַD [f_9Hg2^-"aox7ZIV VW:I 60{ -dpG7\LQ±bbpc[2'Vcmd֜ qWWA vТ'^X[\Yo;WF ~JͫP@lL`qA.Yi۝Gnݟ'nl@k=|QRj7(# ^Y!ڨ#őgIk2UegM(-OkzD),)YW!_!zEëokӵK'N1OSًc=- pvSDqTx>o־ؕvT]HIqfU+?gn xQhX5aC[;$$Qhd~n.egh|MeTTznpCS %-9ʷu Zc%>MF ]Is-}"YYsAf|X$G 'vԏ%&] ZpIp±ăS6!i$2RrMX/&v~~L<κ w@DᓚYfEZِQF7ԽI_.y>`G tCŎ!|_t?1ލ'eu+ @ɺȕ/_ qBDQ4<T N~X;8+ (7\O^*z 0wsm,JNk%+@$xjǎ\-p8 ꁮ>˻h W6?':)9R!۲ekM4~Aekl# y$cl ІoZ %%qHL *O@#̧,.nqCkZHRV5=OvVRZzsKP_[tJb*/HF/eܱZ|TbKIj(}\o {$nepp +{-xgeF> ΐY:yY2jwUժ*X#{K9/P&`3$zTeQ!.p$ e3EʬOJf=~YGY+Age^j"͚FN3PS}Be wmV9 #WZzHȺzb~) [\iUY;ƴoFz9$,FzʌI Q.l@|IȘ2g/(3ʖ[G&]6z]9!D-XUB*C?KE_\-M70ƙX0T(uv1 Q~dϟȡz&*v-we%H#b~ůaҽ)넰6JⴢP3zRIPE&f"liL I-?C6P7;q&Dz]cZ|Q6H{WO˛9Km񲨛+?"PZuŋnJͧhMKh[0ow;LkBj~U ]4B9&J9 u].l?kQdiAqg:>A0Vװj6e#mµ^ͩK@@gsgBY TA}uGk^Hy̋U؂ͷ+ěWԤY\fNg8gc.f$vJC #I%@:j &]KUXJ23m$n~@MEG 7ǭ RbŴVVhLm;<D MG;c !Wwkr3~Gsl3mRpӊL]ihwoYsU?#/*EjLGBC`8$TQ5pԜwYme+GOU2|~DǍ(ڡh I2, ).r0|7c+F:ѶQ_ * E7I/3Ģ8iq!' %8æغ2JcaxNULb5+D _MB$#f2kDw,ȧ~gy^F֬Å{- ɥ7Y1n7 ]%dt 8N+lT1gwf'aPQ >)C:ݽj]kL?c˧IНkJrRS2|?`n(:֞:}G50-S-~svJ $nhQ`j.Ƹ02!y)ҩ?FQNBk q])@6#y)H,y~[i {'&l)x2dXxO\͹5)LP d#3O;!`r8CVK3 7Apu.mEo11udD![qez\OhPq 5]auFS5<;OpzR+<矧dӯgoHi4M3濱41.[q-w-V#V#RϬI 80k$pWpSV1"=*] mz;ĞLf&T1f)Q 'j%sQiv>3jV.M@u~[ڗmqk|p@8$xdyr AJK:`qD 㞘<#mC-BǝZbJI/O_\)ʿO)E燂˱"Uo'@^b\T.~`XFXO\ovc&Zr~r V BmC2%8/=@3&Z+Xѫ>߉XSN',,"g ߋԧ*ؠs~-_EN H-#kl}~KfN8A4QumSÙo@f'-L t͎1krσA0սn T䑏̜q=_~*F<~DpKQy)%ԫ#G8p>4EIEI$1ZD_ 'u&]lz5o=ЋDM>^kcJ:a U7S25Be3İ̜ti]svͧܭX$ϙ?!`Zj}CuJDTMbb.&T:OXɯR y%NyBS36?΂YEm/ | e;$eYfG-螭F !>uQ~2#Z 7D76Q3/.N(O|l -hGk+a.Yثn5 +%7/>ΛQykZh@Uݪb!c.='9̖Y~Io2H ፈ罛KZ"6H3%K;}_/]dq*>Et̝ߪbju'f̯8QnTd1Ѧy8!ߤiv2dhWRH}!x-Zbct]GUd:)v,mj|CC,'rQ94yFA CP 7c/K0Uq$}fIYҢcg" pP~;Q;E^ipNa6[( b:&Gn,2"6ZIxAQLiضM/п@=CV/5gm ~A鏡=X>X)u=HkYls[*dz_]+E %(+yEʨ''s2V  cģ0O'pƫ7oOsyL>^}|1lޟ&/uE `* B%4}@ 4o2Vv˪}E{bau{R4d?|!InҟQXiV嶅S99Sn[wTY,ԅma#b]Vr/@s̙>q.YY R|Q騃p&T0WtZW+3خі֏8FΧ:ұ轸2$m?Rz~ߤ+SSN`ؤ?po&JNqdZ&r}re/^kډ~kL.<Ů?@Z Ox#a*}!̖o{ZFY@㷧f&b@ϐ#M.b,8-bS}~!d'! @SxYp:=!Xѿ~0S=O{C`;j)nת!sv k8aޅK4~GܙI2@݋5  &\>Kxvvh !Nn;2+ \\yP;I;c7n^~$!P]GgTۦ֩00VqFɻ/n*ǘ1gLһ1mn/܉2,C 'A2Tf Kd&(",ʯ.k5[\뻘h֏g:h$c/ğ|IbZ Oo# UpEpxWnX@܌Ϯ,{/(^ ]IL31':ѪTɌ1w^ezYO pn]&ˊ}і@#~Ba[&qb ǐP |FYV׹wF?uliIKOX 6\@@)B QF1eӠHɔ 3&(<1FxLor.7nLC"cjɇ ;]`im5 TEoq2udAY¥Ǹ/T1D揽%N2*}XkX ba[ZO+.JV Z04k˭8zJ%t')_%^x3qﯘ c[/PxPPe &ھ{o^RiJoz_9#`2Y=.}hL&}!֟MH*f"<~>7y&2sD/lvOI_, W֒,X AA|"ךa`XwaJr⩆4xZ%֥aU#2C#`^аQj}HNW xòo~17=(N ܷD1d%+?68CYFvR!q$EЈ[w/;1nVOk(`A6<-dTIƤ7HSR<)Nδa_\66rQX5 UcsȮ_ 7BQA40rZl,{GW@8 :Ƿj퍜pc=m2$ݯ$eX꽡ªOd =br&ZcCw* ;êWFt#e H$as-'y>ˆ 6 S"mKKYO<ܖ"{kČІܛ~IHa­V[`Qd%[h͡tA&OS(t$R=ܧX ېVX87Y\9IGo"y(oʏB4[Iz?>_|`%͡r4g>FPrI5QnʘZލ巇6]Ao|a^v9BбR`[;7D5TIt e{ _rojsēdVykNj(WtǒhkZ ÿ&x@)Gd^3' ;>`V;ĹH$n˭(ʮ_$6 %_V1Kϵ~ &hӟ=K%aB-P:v P~ǓW2Hz2S;Q=s@W~S᠀zAGLV8>_r yݬOes.% wŇZkJI+ st+CJ40owtߝ*EpZf&G9vyfC0M&jusxi1SsֆjSm$h27h9UoC}zv8yeeSܔ'Gc}J&WO ƏPs:Z.*&Ra,:{}xltq3G۸{*"+>ڽW݀^g ն\Yv~mx#iO98W~OT>.zח!DuD ݾģ|)|ݙ4C=Li0bF*$tJN}(vB_Eye1X9e.k 6 z^hί_])}"u|9 ĂFU6Ä8W4ZG>\; 3t~7Dj3=3Aީ:hkϯ(A*pZvn4Nݔ */WS/+͑S*LLlF\Y9|͈Kbu7/T DϛÐ5 1;.4*\`|iD pB8$puˆVLmCoK8ZH`*hjV$ v"銻qĮw$@qN|L" 9"diޓ˅  ij˲=XdWvC݉J˳Y*3ܜ-ќUik65Pp1ϏF@(5=!2+"T+U;ΛC#c\/>[2UDEE7rw 6dM  4zjhN!5H|e ";X24%p+ȯDV=DŽF.efGAQn҂,1ݘ&MjjGD!wmvT?Z닄&yV"{kHT;*灐M+p6>7} nʶ]1nӽKS`%B nE. r9A7X3Mv JAYK5mX&#vQw JuP):;R.?zQf% a/ΰMO7)ŝ(]|(e$y Ot>y q﫱h& MLpARq`_+$[<:cFő~,f/?Dqj-)"6ͮL_=?I4\%w:}*8q_jNg%pdiB?aILY5pe7QxىC\U[43a[/Hm!>nRƹ/p%zQspa@7kI}=J$qH"kgBx5ш!}[MU! pwzlyeNR7*ՅALSG_@[yWXAG_B돂bg͍Yj*yJL PC8nqǼ.ih4ds☕˫VسU|%0|0v5&سaq-d܈M%IQ]A >'{Bư[!73Si8ʭa"kkz\amZ:'={+pe""0,Sg4x-paFF|SG ruc99~r{V 'lGᐏϷyEC.O,#&B#|Q+s-KdX]cEї`G6/H8:]hE$fU}Ro-ZJKћ oUt38 .I0AG9/0Y_sO݋x7f(F,7 FZⱌ rDeKi\hN9I-]T66n/tv1`ЂLt54HsS6_tRl s-ǻ 2c;L7yZKۂ bâ } ^2M߀7_B g Hr{ iʕ 5(a)%6NԕA=%?R2A4Mg8 T{Q+n 0bJg2vߓ?̎VLnʅW [XKM1. V!73S0#0*`܌͟yz.; LdsiJ.[u}@p-[țڠLlGp -vp>nsdF2б2gSibfG bo o.sB֬afFFa(U ֘ܙ`S&*zS'Z >4rbr `/Z6=s=J fO}+SPTvq⃥To\aQDK6wKhuXUi!Hk5ê?-ݾI?fg$ vSâLTmo.YfEZ85iYX` n=dS֪=0ő.qRkSA!J)7}mɷ%&ݮ~`ዂ38{eYJ=5{9竛ly&*zi٣ih5iYaL_ȃ Rn&MORDIyr\գmKVHCe4$ Ѣ‘1aғ@~_ 6I6$B9tIYw2qXVbbMHtSSCD;{m)I{$`f3R.QqXȍYEM}nXeje17i.n>{ӥxɐв[ P>(Y  +uPy*z~C߉DZ kЌ{bFv[x yt??k]窢a9@4|E3f*7nB h0/7 TIRzƊ&gEyNB ?g)h udpVk_5bU 蛌[󚺠^QHJND9[Ҝ[`ĄѣM skM@%MЇ%V{) Y"]TaaX~̄եtQ^9oT9DpK9M5lgURv:BE"g~6W_2Wwצt_riT 1#w31Ezz{b'E50J=fvϵ{QAQOVA C/H~$/}4e3ڸR$>5eu\Zbuդ6v ;3ô!S(Hj#ƭ &zEvz_;?N3UJ Fd$7XıЬqVo.zĀ8 A}^;@(vHġ*A#+YUI'loϠzJ2d aй,si~Rär~@q`h+M2F\թ>.QӱKwW+zgDפ>2L=~"UzN[_tėKb<+7 s)O'?a$~5 [W%i.FM>v3Zd֐;K[/q!OX?>┳Cfp-O[~=r !",٨,#DƝ$b<8 k+St뼕x@rƝC}ˆw١I99wh \9}&W<#abؒ_ ENzU!Q ,n<.?3ߎ!N$ڱcqdYz(y=Y֖ԶZL`9rrU=vQ@ر| *y=GDT`S;&/jO;&˙!6(hm= LG壙zNjvhNWY@)2ʪҾ!!VζyI7pOI'i벂lHXO0u$s"ҋ:u0]j3%3TMP̄[do=wdTXG5XT*ѕzUWmbOO.ŲKOS2鬹LJ%..!Jw95fROKfnrxc)&Qqb3>Fx&#.~}.]=3jM4{ʢXHi=}gFo$NXb`G3$ZH *IOeU5  |u{H8(9xc9mcH+L!&U1X80T* nOMݴ%~(@]N+ՂV ̺rrA":8  6?Z:W.W< W$tAY'3=] C:H5Ծ!$~g*qu!Jz7Uyb8Vr!09f( !t-gT T9xaf-;-kuecb=elutTaB駵aW#xA1GX桯27up_K퉄mbڎonZE˜© qo$ܫ r)_6Z])c.?%Ji@_OxhʂT%f@K p:Bb=25VPp(S.S)鐪&D$(bhmWLh+hk QnB+$kծ'&{/R/ .FG5ur"4vE8ʖ$;E3N!=Tfs%kS/jH‘spʈҼyVR.94vʤ'!bhd9?G|eJWU2xe]nCd;Vn1zE_]&QkM*m@i7"T(}~j#ńR@^7z7aA 2lwݶYv/Kj7z:ed(\9CU=@YeNg&  Hf)k#``>y\M@;eSRąSh@5GkҮ[ ` v|,4>XSNĊa WuGΩ?;}17B0~C6)^P=]LJK[zM-–2v9xyLg;eII Kt!|/>J}ǁSS>6@P)D}7`/A"xh7ɉR.yj3Uu+R3T&p*anQJH6zLb̡l`϶׺όm>sTCЁ b ;+[Oڦ=48V15/)՝Q oekeb3eQYfN+h=:rϨKGq5jkQ?ڠY]@SG XGV>ɤͬH_ږY,nK*b{ˑ0}q Y8i)xz 'a*Kz_8 E4R 1CVU1AaVg/ja^2O m/"M)l ݸ:%4nY#J&@6{Fym+(u_gCAkm z͝BI`y:L 4u4O)2gB  eot%5> Dܙ mj Gـ^)oA(9UMB;̒djXXh YǛj"xeFe h׋?0QOTr^W;keO50yw_ 6)˛lCChԺ!7pQ5ߵ ?ID=oirwaK ,(5%$<͡\fGXl!:*Q\NN劊z 0` zR;ι<Ũ0sY[(muSHIW~@%nP8x@R>PO*kZY8E*YF3b+Uָũ.7g.Uv^=zPb•dWwU'{ ^z2**o qKw*rq#k.@b5Of(WriI/_|ˎ΀` %[^7N]GN '3*\ ̥hK)۝jY3:bm3B~3;('{? + 8Dl?G0XQЄJ@_;oM׽xkUC>.LȓBE L֥Ɵ[`S" .ePb_ET)1J[Usĝϫ*re)-(aQ`;!%*,;Jyli9?uyQ!HRwb!%]D0>C&,9B0<dkBO1B{l]=RTdX\ /bW13ܤQzTR1A?l ˡ3w›= d?i@ݸΑh )3!P[`>4,EJ-.WBծ+f HMn_,e'UJ`(]7ߤSX봻*qO8ޙ .S2t_]S] yh DxIrX%{Czvu}*ĬI^&2׍ ȦRnFF c ++n]e@.dybaeJsv1X}='X)㺮FZu85k&*]ﲝ奆d[}5sJ%n3NhIj<|WVKPrp"UBi ؒ:"dg-i6u{1.Q[*5 _ֱSS9F?iX#۫5~u_2xZiMkDR*cRm7h; HFoTl\ :kVzzBF{77`W/hw}4z`G=OЬK5_N TFmuڰ!E+([n[GoUo1"x˫j9ޝ% ?48ztN|tJRW ny1N!8sQ2bw fl<Xտ7!#/8!6'Ҿ~\Li&çh|V W}$sȞ۟DN'z ܝ9|7ЙPŝ yF|sm)F]Fcׯ F2Zz:dx]y汢`Wq|EnٖƹޞYw> =0,?U:u.w̌ e[=R9Sql}+;bm:4<v٭ GTiR29 n h9n(ƓgE5&fH?'] Vp3MFD̮&.B JqgDѝW^-i#JG(5uiX" !cO\z"zI &z`Ҽ+MS~|-N\cN6WewzTcwɩ&:<ǰYOTҺp u>@_Ke=4Ԕ9P _/o9ܞ )Isnfۙ>"n>{ k.aVzLgR WTwn*eĠhõ />4*Sn&;( u4&ɓwMBPdJ`>ߔ@Bb|)т-ArB&}sƗ4_+״q` t5Vu訶 #XՒ6=y(&}^=t~Ġm*Gx©/܂<"Efsx7v~.»S]KZ9YW⥐FXt)~K~In߅9`:YxJnOlq3@zZZ4$'g]1MNI˞Tb8+k҂Ya`uKE pI"y5:w/tz$ۉȻ,޼+Kqr"çz'5sKp@zG&scQXFP>7 (5zkdG1 H1N3ogsEλZ)7Li{PBs%b(KlK{Ky^/$ ̦\'%/IG^.We3{CI%ۣZch@F-h&fbWg/p0A 'MFQFmcjᬂ {S6K &ΕܢO^뻐$iRkvvVA.5*}TE_;R/0FA/GG|Sd8Eq:-ۃǚ 5X檫(HQr,v"l#οS={uܭX}]LMhiL3a4$ݽEZ[JxDel̪8jvEwiD@VH&wlq&<۹?=)zZ!y2amlON;m"d Bze;/o1L7IZN'EyfB3 z? 'g'hϊcE.w};3MYC% Z\|k}۴TxhgvdJudo_Qw;s2@ >vt qdѤSzG |)mBqMde OI徃k:zPJR+T"#t%Umq4lɣ߫ݕzsٗ1 []H L#VlY}'mx r7&C,llLngBMV(i*M_;~ňCmH>Db[F#DI`Vt зt@*'4ދ\;Jsiͯ !i18>>1;@Չd!,ʉfԶܘ"cY\3Y* m9.tPN;~o 7RJp]Q81JY?T]zE2m$*#vL+Ap+*3Tx`TC#o s|'ڷ`,&intAiO~ňCSʌ̴{IbD& 'u;V԰XΎLUn]<7O*вJ2TBf"CS~Vs7G`E &Rm=ɚ2+9P,n6RTGhuEºD!Ģb&DA^Gz!KG8Ц$4.b]rkfyA;DoCo{IJ hծyQI ݴ"}ѱE4ϱxZƈu)PpeaQ+~r㷹ɼcٮ[޳CIf;$MDIOE d 2,o 7HT)Ο-BktSRVKs]$h_jЌL|@E0~8TYjZ\9,Xʼ=$qS4.*$V~f6hځO77 ROܴ^ b,ܮ`G-%:rk9)O,Cn|2(5b>,[za+x:3ТLx- 0Y8%8Br!4Z,ZdM'ȋ%\y^b*K 1ЪZv1z"PIDP~BcxhX'd ^8Q-'mWWRjw #}= r*zױ(oK'%|lP94P/ ?j|5yKh P 8%Y}9Q>UqMkY&zEE僛Jyy@ +S\`~r] ,z|2 q+9wMa$~:3Kesī䶟13yEmЯ{ }a0$IgUH<ຒ' ʓ>J:9#0ʴ`h߰/3^-6"m/64Jbd@u<ܱd$鷩 waQ`EλbyjRpq)^A +}iVN`?a>_3>_Y,^,~KSVU¿0PrYLw fyj,0u|9;`>xy^ߐbK#FdZdbRӇbIr{u܈ʾTR5wsEn`~n2ч 3gLJ'hNrNn_'p1[1,;=c {G߈EO't3;<Px@Ig q)m'D+n^&̪S 24 <žcYAH1 ^3~ԥJ}-<D5̈́]𚵞z7^|DR8y0`>Qؔ^,KHs $K҅kë6$ZVqH3X~jԙneYaG1gp/Q ^Y_R `-Y,ھP(եIEs@uz q66B]<RjKoD%5 20"Wy#AdBhHϿBLZw=SX n<-"qX]{žK<8 ;=A i%bF_E^n2=!cX 1^(1}P4mIX'0lҍ)? NKOеB #lTP TFboV咢q !NzYM*~ q(ďr"YHZ3osP!D#fˀqX63~2'w{浪#ѥs2=l|;K,zH\vf}Fuuk:srCT[O`֛ܫ,B/RR|^~6%DƎ=XA(0nJGٞu(Or`Rȥe $oożTst%5'| MR퉬$DԀuqGe9ce5P* ǵ!d7) mxrgc" W p6.ދ+^us e04;юxW@lX9]̊=_lFB9;_6F\"C!dm;W3}Ej\ 7V/RJ};;AZ+뎇QߔCNb?NIcq@Wv\Hv"$h{ђp'1әK/WTeWZCGa1HBӾ*⃯utx^mY嬦swhw)/U "#ns2CxՆ3He[cX#t0q$O.=Eh]Om$]DQnH9dx5l].S_.bN' 9_WR^+;;K'd\l۷;k/ǰtop)SR>c :19Eu*4@Ѡ[PZmj64D%Zn=]Y}7ԯVfF,f^X518Fܙ|P-nV.A"i{cZ)[C7 +>?eJa\ﮦ1c@3)#z  rɐًpJr#-/^h\ho"CS.OC{d4,AO^-v, HimT)WƺE+ #ڰCb'=tѨ "VJ>n,j?٭1zvz%3&gyzkAyPe1IX;ubÿWejBRH4Xv'y[3ߙvaO,Q^- t7D"f":4ꂇiG+5Ct \I {ڸ\g>,`6/lP Ko9LWp!Sa^0nVfnr_! wuᥨG#=%l}4HO=DEI0NOd*<]Y8 Z#G:Q wd~SS%[$=jTa:VȪ܄ d- М7C(6 T* WO 9R Wn 0)^u*$H+3w:q(fVlpWcы Kf?t' ̳OB+_@EFD61>|yD -Q-ҝ7~D۰ CX<5ah*a& ص(y[7# #HUpCc-7| t̑OYe?$SH@R>Ii90ðCOgZ^  endstream endobj 174 0 obj << /Length1 2521 /Length2 19150 /Length3 0 /Length 20641 /Filter /FlateDecode >> stream xڴeX\5JݝSkpw .kp@Я}ޓ>}߇1u̱Z(HTM팁v gFf^ȎA̎@A!4r9y\Egp# NS@ladP윜n {8Z[8lF%d ag(عj;hadc3*IEu%UFpaU{{;pUUS +IuUՀ 0sz8w8 ,WF fC jhgW=/39_,,nv#0. S ~&9K ;Io-XJp_b`!״;O #r唔F g dt6rvqeM&8:!.?"vl|Č@.N69Y:9;]0fY +HK1ȁ oVWzbrUK*2vB-%X'g;G5 1YL~nbϤtpJ'lBc3:@Ăwv巙,= ci x9Ύ.@:!pL-Mk>*Ux6? @1QS;h` ^NٿzI(Ìl-m<'_Tmltt*Y:X%; mvFf6Nֿ=O xu׏ & /ſho&u)i_Q ;SK9`h ^V xMm d Nػ8~?QNo߈ $qL Ib0IAl&?A0A~`R/wWT p??;hgOhf0@?ZF.F64/4t;ZVGfEDaf&4Gs)5Q?Hiy.VXvE(*ų1K*!ovG^ 7J#kn8ѢZo(Tj&6|],-N_3gތ!")߉f춧|Ȝ]K+6m& ;ß0 #>¯c:ɕhlZQa$eDxS;F9艍2vF 8Eت{F3}3ъI>SnpW.qTmyC^}6r!9;,: JKΘpb!:Q6>Ҙ3(NfD~7rT9իfbN͍'Tdht".L1k$I&+L(^ؚؔg@lo#:~@}zpa^"۔6ݯ/J֤cd0?+fXj3DedN6:M(~ee@4JSR@DN񒞖y!R܋PT^Wc%Id&v[\~l0i3+(K)#P(Q-%.nȼ5|xSրQƙ&])$X?q0q1q&<]>1 3`ve?}PR0 ["Lap:Hs&jDk}^ceuxC\rNPQWfgD.#xa@G^]#2]{nOxv oɣ̀ ds 1jPmyRT5\+_ -Fz~ + ߱|HU!pQFk,Fb\g-iq 'פ^Zh."{GdڳFFY|pe A5ݳ}2`d"S/UZAoolT+TOov|頻 nh.wɡDTʢĞ>`޺F|D?4sk]tE?exdw'Yz?1}H?J6e*~5 gE^o-k tmK23 %&UPbۂ!jS9"&mfEV 'N uB= u%*}m׎?Ine\;T13L)Iګ_ K_% ]_)fh%&WhM+B5 aT<|^y)Y&SqRNςkp+ZFحnD3.q1$:R^W6.u!Óvu䜩!'"F$͘ %=A[ob})0!\Lb~/^=*z+&DЫr܉}kV%paJNE\>؇Z3m$&nz@:)Co%]|11&7=fSuHN!^0 46}tە˺^X!@$Q#>B^卤kk*/#tF|"?pg-bR~ӯvt1ݽ!;&$'a7!Be[ Nn-t$ͪAl Y3Q:}NN_{&WCqO(ob]p{08|2f|5P̅(G^Ԛ3,n)g#qǶy:wKC:Kmt55=U[XA} 2EJ0~̝UT!ntx1Dw=`/z 1ɣgRC)\"D3Q3>`)J @+U,b&]xH Ɩ:ozI<:T-Dc z-FM JG*\_I`:Cbİ.oT1}a~e)5oO\nsJF:G4M 9g/VG9UM0)W)\)TNōQT7@tS˃VZw2&lف@·> ?\^$VR݇xmNBf!E >hF I<lo2+9-MO_BR튳-1(8>]篔uPzG(5y|k~`Un{/J=N~fNԘ-ٷĀ0RCKQᡩ9 Ppl]w` <;= 0wjͦ~gaywZ:LcB&6l"U"xVS(-abGG vA.d۳6A'Pݡ=#ٯ׼{D!w+OU/u̢|h,si&Kдs[fɂ{44AiPiP#t8EFxhɍx +υIalqF# :Y/W3H%i <a õBv#,raO5YNUXm;ˉ` MP2eFLG&G${F{O`4|DukvY:VYӢC)MQs%gy)4q 5{_3ǐJ0,Ú:](zM( : 8IlOx==tR.ђ"0q[x=8p; m뢶sN/k|ʷvn/ؔ2bFB Fi.GAVB;LN&|H`k!r.8?l26f:==5\!#RU>*%̢gvleV^ӊud8)C/NC>=ľj B=I5NeK5/5 =jVH>Q[ pj?0IH^wl=^25EJco~@d 2A.r4%|vy hٓзK2U$ܔ>yi 3Cg:71=Z,-FNHT(#}#β\$C-LOz Uf"- }s^eYJ]d5cDpckX}>'I;DgL*m6\PU0!890bo[3uKP=XsҩCI;'\3s>MhbgO?V$3O$\|>y@f\Lہ~k-x:O:1n׭֣-OHAݏ;futZUsB|nђ?L_v"9L+ U|]MT^ǡZ^f+~VZϰZH|02]x$oSGXQx2 Q:/*<ϊ36yVhi0b eP˟x-ިGge|K.nk3f]Uex)~Ja4*{cOy}s&/=ИdDw!Ԥ(5hTp'JNQi"kg _c樥f: hƱrۨQ;@q)nêֈ)WrdѾ8ַklY \>Tb>|޴@ߩZ0Bwh`СI}baZgWڭuPpdmkKε?O@5z+5xۭ|y&|)3اNbZT 9q;rkTƮ5Z1'-dOD茋ԇ1:!RD G*M)(6EId}\-)/=)S;q*9_ l\t=k]a\a/RDWTT,eł:2v'v'h91miJʭ;w_!Mժ'/b"yV :YZfhzfYF> uU9Ԗ}QA{̲l ?AHhp6y)[:Hj{̤phb#m>^8ݨV5G ML1`ኵn`LͨO+E'qS3*ti}WEʻwUMH:$H_fa?qemĬ챽:CK#Es֘=+|⣩O|k(H@I@Uqns5D9@մteW e( ymAvNAΗJnJC#$xx||Ū&  D|$i x;M~s]QK)aj)r;^n.P9hp*|Y!;K3+ s͵+O_%[U%UXV3 À"KI,n ):sc?4n qЫG=Q _20ibIn1mgi_< "8Mȴ-#8|]PIL^*h } :Nj'Nk.{S"o]f z .ipҌZ/~ ;w@i3ghDj0 Vh#J(Cώ&Q=, Jmuku|Ǩ"6qyqK |*U30Nl+ĆAIC|F?4T9u1濶0)) )ԋ,JT%CsfIBÝ&%ElKQw߾/(riğ^(~q@yJjF&P Ɵ;,|Nw&6ĎQH#DO9R۸;}zǯB\ )@t,|/cyih*J:>K4$̈ # 1 fP o$Tϴ067}8g:id̅=h~RGK_?Ǻ[|>S&Oa_ A,Wb]8,\@ŗ{݂%0b_&23-?(^ЃiC+MHEfD(Qj^7aY^ #N>A$Z<:L̐mm43edE#%]fns ^$ـO~E,())%Pt pG1bW(3g?obstk=Ӽn YMzZ52 auJ…&H#Ē-g~}:M -5J mVܡ~u\9LX\i*5xfX'rt۠'DD^Lt%uܧM֢ϐ;k :\TLO=5 ψX hv,Wq=x9D5&c|>~ UYDߩ=m 򋿌VG! JL! f诽S|܌V؋)59 HLa"k!7duS5%I&:ϡA!{<9ucva`oKLɲV3 s.s{PE EWS8 !Vm|zΩB/jM #׽/Gv=bo.6p,h4#Rֻ4ڄL_.vﷲkocPHJ>:1P8DMRuΝI:;'D̈XVl1cf`+ACNX1ҶFFoŘڦPZ۝Qk,ggNEw*tM<% 0cPIWfl-#,QD`*樂sn-25t5UAB#Wf]a)ѬH˶X+X|Zo3sp/B{V[. |c m*7H fNS Up5j86Ӌx}]G`]+)ˊ|Zy$Ev|o>t^p&#?HfY/SF̻ 8ȑ.$ EX"(\ȵH.SV杒 ߝSL'?1@P2UAߩqKjAzf?ͪ>ךn?4Ppּqֵ10n*PXiSBҦ0$Θ5`{m9.2U-I)Y4{/ S%3]A(ދ$ Oȳ>RХLj K}!ōk(J*ZrAXA7ha*rkM3UKw)D_ raveK]KX">duJO1I"泣lr>.Y!!MzQK;S׈`i}іȻakT:S>[Er,RGo/]+ 4>>6ݜ:ѽ[n4$E3B<87f 3_; y!2H .o';+q%up"bQƻz,zEVAВ5:7MA:1?%EW-VIl8x/D{9%@s ȍO!q} m'WK7^ kuW9.K֍Vݍ^ ky`ER+Ih]M4g%2#̦ 홪k gb ,yQGa!D!`Gw#)Ka9W݇Iwy׷-!w#%?+p7jsmV<@@[a/u=q|@Aۓ1椰=3C9az^U3}ʔS<9`63 _r2*lv ~'ޝ'.9u{ 5kR3:xّt$_It[@uCo(2>5a )gSYjq~˿W= m}^J2RB+Y[Y5P݄O }|۠_'n3D1K8tJQ;;ﱩ-lU<ߥ,zJѳL߈yg''mRMAz`ev}M8K0(7uy+2jH&,ް[ΰ5:E Ŷ#8XD!'blRr}]3=2q6ט "~ \w+QYɝ݉Pda80hnS!m5Gz|e |kFEM5)V,' PQVfǮX8<&|n]~j;Ԕ˚+u S҇rFNYV3ڌR窅=! X}{.P 3 |'4+nJ2j >pH~rk3'f ~mAzٖPN'XzԃS1}nR1ke} 0$E=Ir’3h~*;r %!$|b$&)ǦA}/׍!ٞ;\U4@%T2Ib%<%w<Y$y1/ ښ];wWjD'b 9)` *PBw$IhX 'p˧,8 .2Ęnå=rN{9CUTͻߏ|:ve^MjQ +l~"!aSA~F="u{]|@d>+dv}z|5l&V-U))Sjӫ2O(?5>?LY0l쏳kYLWQ s/FxH}Q@Ӱ٢/R- pYC,:RB6mlm!bW-g"5rJطOyGF׬սw8BOlqq*?n}hFm nNdce`bWĘוh62ز?kw|-laz# i V sN; 8x$2Jm3]E+:^eu'egpf>5v 4LH''[0|T]WNeObw/`܈Tzjٓ8 ;і{Lp>U=,!uUIUMadA7uzɐ*.R;9VRܙ|'d Ɉ/{aRW6G_StXU\] J>.AM_CY>_I}ؑ'D߱/ۚoGY( ҿfzRxBchEޣVsJ)kܩM>ܩA%̽G\J2B8U-6* Ī)̏hSE|<'Ejs M7'J[ŀ< Ȍ3k~\B|l :\ g-=2 ~ݝ {, #`RJ+.⊓FYyRyhpf@!tώ[,XםѾ\ !? ]EœA ^l u>@>~U02 фqwtSy=yTQ׮=X7F?I3od`D3_^`w_ t.DGv۠hDf B{]8Z6kT_- (!ͻ]*l0۬*KzkpH0@ϫHd`dUīj|t|ӕ y_W zOxPᎁPYRoFɽl0]4BY8&IA)7i/c1&!Be*c1XT!& +Β >L_Ѝ-MtCf E^bPy#n'KXss۠TF0y<`;?r; c<6b؆&УXU, ؿ$XcWblM5GTؓ0-6KjNM.^cQuVj#!!O# ZE-X=îՍt9W`bd"d3I;Vbn}}ơ1 =t$ڲh׏r=Wjn#A'x_JwN]$^sjѾzi>(9ZzOnxwFJK_6draCqq[iX&m*+up79ѣ8Os#uo-=~քv/ (> x] A>H\i/Us^+J:3gUFGfdh겇6t?iޒf4?+eE[r5ՖVWlg_y(9rH0.;Stg'ɒDg&J|59>eKtr( Zq+HM p':3٩o4R|9c+\cU_YDZ"4sJGD8ŀ4UhԮ е6jDU-0 QZ$[Wұ 7~u7D$CFc'$:~>H\I,D̷ZE\qf AF1d B&h0_<"^/i~QXԊҌB<F~گS.I(a[Yc3 C~Yտr)8'n.9Nm7r7XmjFybg(c~ڲ7g42-5c@>AHsh@ $.·WwkIlkk~ۋ2}U7s=;5nϪ),_#=CxhJ>3oCzfhwUCͲu1`jtl0T5C:b^B?G3hiIJoGð)F.>|}|\ CŮ@\([B_ ' _Þ{m..@3^s2,ʜH\&Ӧժ=۪HP.NYc0 i+$r51׫4Ұ0 qDbR9gSFG72Pe{uyaxAY,e'5\f!(,;Y7P "Ũ_A5Em6mQǽk' Qhơ&t:}rK K%ҒAݍ+Lh>Z T&S# S2Pk:chq@{iN^*[U{ERC=LK  ig[O˜P GϿ ʫ@r.lT)/F5*|)^C{ 0V!( }Sޕݚ]]1i[ջ$$3%}%=+-fu7Wsi #^@S>XOS oK+wS~CJ2->ʹ6=rPaTy{/ ENK8Hpze;e% R^h+WAKU=͉*1y(llORM+}R4A cG1tK?'7AOyIuz8!,#qw~#_nUyжslnuZxx 2b:g=hsZk')ebIO5 ߦ=2qܘ<ɼٸOAX4 Io>Մz=ӭmT9I$~)ڸe2;v/tN]$j+O6 ^ž0ژLe.>vF0 tT}ӈx$hZk?Y}{9*꾿'ol -qm-6*CE`iĉnvλ^R-&,SK+܁ċi>2#,W7*rTj7J)+߬W:漧0UlE˦=R0Ud[ꈬZ羐@:7f[(ȤRP~W Bۂ zLy w\@JuiSShhB/Vj .[G{[J ڵcݭVHFIۛhJD&w ĒmW[Ճ{st*!4vSqS*fhf8^ź6}' 7w+l0_"+_$q#DZ!&={!Fw~T\wMZj?GXyS.f#6Yg&!uzb"QIHd[ى_-ک4B?GML>zK6y+HW{:cկk/qN*CXcގ.Ox7֫Bg?<\@pdA>4>2^XvK/ZuNjj*:&RP-4>v7/CVs{kz15 ֗dōxb:fAtV 4kR*5m$*S7nAohv2>׾G ؾ`3m6 дwq**k*1PNU}2Sj%׃R}2 Ku57  ~\w~j+} ,"CS]_%;~/TN< 4E?jgE\zϲ$+S CƺM謝óѹ6-i})9Se X/8@Ao|} '[J/à!t_BS^OxSܻ]|~%r!#Q4="Bzum[Ǿ`hq/ OMs0P@Zb.e|4uH='"WŎBTϘ#kG2̜iGWf2ۑȓib>?DjMW?,$:OD>g=lNd)$G2BW#*1AqevXe{+[_^L&W3fX`pHgt%:3`^᧿_:85ˉZ-Z6Ғ:)-3q,AbS+Wp? 7JwrФ<֟$DZiGi+#x&y\b.JxğRݾsX;Ucw {:Ņ!  AA>25*ٗ!V7m!pm&%*$feͪ0X=˱F+V;q#0%B=h)#9`?Ë. TfW>F = b? 1SwTz|3@uli)8FԹ.jJ43a[cNBҭb6\~&~,, R]#FuѠ] QiG[^'C{^9B 39JxKI.)p<=5MYJ{, ,= iV k+a Ow{޸tͱW9&.MJa iYWua=析JД- 1$ߊ@mTQJGɑ16i_R+:4&-[,|;{+cѡ%H$nK4hr-Z#!)~bSp`];#ߡ?Jl[gx'R%<rJu^Rl6T;Zӈ)rS͈2" KFLʌH 1C8p(񁉢ʯʏ.-,TAU^:8u`3J7er Ӂ;u4 `UdzyP(oL"z՗5dI/OsCr Ɉ!U741V kOd+{d(jc&17~V9ceNts *]Z؇ D>&Uo5×;cGF"7iZi>y<?=Ihb*MXj&?ygyR&n6ny$xD + =7XS:U:R̝_&-GSaf7>G;f5\2^&#dR2tQh͵d"CpQ@R~Bښ(yJ^=c[@\4L")%m`Ya(M׬̋@S&Pqg$Z _)ϋw5iDɹݩASOO~ׄ˒̔JTUFB2<:.dIj@[t~0Уm-Чu .l͝ouWWAP툦D]23]2K[0L҃^{QB RX_a3@#:#1qJ۱,]ȶsm. oA_怄yc}sdi;?zO?dbپ,!k.;<3@E7-75Xg-S,O1t u?TMGrsߏ*l ӛxDG#H8H: G 4X lG_S5 }hV?Sۧ; ] PsVG.l65OhT%W@"2\]FmļbX2{@%c9YrqR[cKj !n-^樎b|@ n(SF90=50^  nltyNۍЬMWcu{ZLXn SUWR'e61+W= d;b*nK}9OIG; lフ.*laԭ3: L|PO!(K8h!(Al{YC4<Pi VR=f^rE)2R^"PYnB/PJ^nCE1[dSM˝&8p.L58~oh܂(zBgH!,l`FO_@mAK U,msCEKMmχ-8;ݚni+~*EWl1G%vs8%;HzbQ/|^doMY4>,}fSvc/Q~bd!^P. J@fywCc+~:aCVm(-s"Շ%:qwEUyXP ?*vRo` z*QzE^ "O29;K{@.KD*0VOuQm5mA7hL)qg$حnQq<񰣫Jz]:k~--kNRTr&;#R-A,:m2>1rkJll[ i{w!}EJ; .g# >0h& 1<`lƌ~/{O0J.0ό'E4F.&~>ߘj W nb$0ſO8L[ig `FsG݅ʓ:un!t;&PjIWZ9P ۊb02sk"Jg w> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qhe9xSTQO7}̽][GLrFMG}}"Im!00jEV%H^/v}0<_7RyL U~C`PރSӯ|' imXYӷ|4nt.kP^k?IFsuB`nlB =@ )U 9yI(ѥ S*043``ÍSqf|kiCc, pDˆzF:x0)ljsn l9u}SrI4"nXCA8%&ٵ6AI cMXS?S/w,;: fyR(#c^g!ch"ƨ/kC^d cRx~h K^| МQV14Ld5cY9Y?=C9돡'g ?%>O:ShYg{ΈrYgDk>[bghX|&^V|sig33qgng3tZ[Yog,g-g B|B|\3gg|2?f)> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qDŽ r Gէn8A{,쏘LEvDB``B9zK~;_q`>Wgy o.>ݫﭯAbZ%?6G_Nzy;9ڰoiܰ^]0zu\~3ݍܥ: ل0%1 " 0Z{q́0R0r0QK5<T`,if,1gT Hӆp1X:,p8}u 8alSM3?r>x\i"EܰpJMkl4\?ǚc:#?^YHwuprQF^odž1BЖEQ?1^׆ƨАԗ039+ãbLi~jЙ}s~zrCOe fYJ|֟uМ8gΈrY׆}ŊϘъ1LҊkgigϘ݊og3f3|3ߊY[3 =L3f/gd ,' f)Rx jb&'W *.MGZN(:p~7a?}]TyԟE}Ư%Vu'e% endstream endobj 178 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3FXҝA5(O)suߖcHQIܮQW Lڮ9ˊ6nK5NoغWi~r<d(Vu;_=85vѩֆu5CNmm悥+U=#)\][|, MHS"#p #>y| #:##0)%T\`YQqJƚ`ci|1Mَbo4m `2WQ/cW888sέ-./qJ;&\ k(d?F#h0\?Ipa]~9Vk?q1Bx.BzҬÀhƘ'g 2xk=6u2,bق6E0F,eL燆LY` YecODV3Μ蛳;zr֟P.O0{S3ux9(uF: }6,V|ƌV|gegV|F_+>O+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z endstream endobj 180 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z6 endstream endobj 181 0 obj << /Length 844 /Filter /FlateDecode >> stream xmUMo@+H.Ȳ|h%Qի ۇ6웙7X=Tg ]NncT5ob<Y:socsOPcYB?9Os֙3\Q.4ڰX3Z9#>\Z} ?L[ V|V|oV|3[: } B|)W|L| ,Y a!SM~W,:?8C8…I^U E'b|82 8ϻ޽YtїkZbu_G4 endstream endobj 182 0 obj << /Length 843 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N7R!ݪ70W?g_,ɝиYs{ ]7;׺v=ߩǡoݨM'opiT}IAu~\3;he?<{Q%(SVk-#&9sQ擾ݾk^!00j(+m$?Gwt>X.oTuþ{S_tpСtZ|I1?H/'BZV;ݛ ZԲW/{FR^ww?U4H6!L@@B@q\s *G|F/+>㹴3Z~Z83f3[:٭ ߬Lg3t33 ~!>CO!>S 33>IY ?BXIAup*Çq G潪N$p|eO_:q;:'dE_kCa endstream endobj 183 0 obj << /Length 845 /Filter /FlateDecode >> stream xuUMo@+H.ȲrhQի C}͌6jo73o{q3fѭVO4cpuU sk/wOwquy_t}??p]AAu~\33cA}P>>%t;en>r8`S0Aj~vUk&Yos yv rOiHM0[7v,ܜǽJnkz~lNͿvt*amкq۸qۿ`J-ztH]{O|, MHS"#p #>y| #:##0)%T\`YQqJƚ`c2U{;5Ҵ!\,18"\aD E_sN[sS9)9^W$js7 GZ ׏p$uX}/S/w"': fyRy(#c^g!ch"ƨ-kC^d cRx~h K^| МQV14Nd5cY9Y?C9돡'g ?%>O:ShYggΈrYgDg>[bghX|&^V|{ig33qgng3tZ[Yog,g-g B|B|\3gg3?f)O5[TT+&GUP#a#7q/c?z~#袳rdbP)n endstream endobj 184 0 obj << /Length 700 /Filter /FlateDecode >> stream xuTMo0+J!m0U !mTto4j{zv|tv ںQf|6'op݅uM{}ugfci"Amƃ}>,%rtPRJ(:X'Ab~oںT7h uSӌ]Acq`sy̟M.n? D`އщ7+d~4Wj7vw VRŪ,ׁk/bxO0+,F )1!Pp #]QxQTv)#ZBYLt/X^r<1u%pr_d9٢PSi0@WQ_Uh֩h諵"qFM]RrCpt39Âж~j3Fezp888Q:1bc7~}Hq('bĄ>^m# &zd}4)` "H,4%!%AQ߄B[B~)ҙ́ _)M?DM;豬;kyoQnNRd\Ӎ;WA} zoZZgbT$Z|U endstream endobj 200 0 obj << /Producer (pdfTeX-1.40.24) /Author(\376\377\000N\000a\000m\000i\000t\000a\000\040\000G\000u\000p\000t\000a\000\040\000\046\000\040\000J\000a\000s\000o\000n\000\040\000A\000n\000t\000h\000o\000n\000y\000\040\000V\000a\000n\000d\000e\000r\000\040\000H\000e\000i\000d\000e\000n\000\040\000\046\000\040\000J\000u\000l\000i\000a\000n\000\040\000Q\000.\000\040\000Z\000h\000o\000u)/Title(\376\377\000S\000h\000a\000z\000a\000m\000:\000\040\000Q\000u\000a\000n\000t\000i\000f\000i\000c\000a\000t\000i\000o\000n\000\040\000o\000f\000\040\000s\000e\000l\000e\000c\000t\000i\000o\000n\000\040\000p\000r\000e\000s\000s\000u\000r\000e)/Subject()/Creator(\376\377\000L\000a\000T\000e\000X\000\040\000v\000i\000a\000\040\000p\000a\000n\000d\000o\000c)/Keywords() /CreationDate (D:20231002185719+02'00') /ModDate (D:20231002185719+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/CVE-2023-32700 patched) kpathsea version 6.3.4) >> endobj 131 0 obj << /Type /ObjStm /N 63 /First 560 /Length 3593 /Filter /FlateDecode >> stream x[YsF~篘GkH TReNlŷr"hTi*v1 3΄RL Lpqx&OǤVLhΌ-Ѱsh6n"pN QAG 9 03Iq@G&f2H0%c\L%NLRX0m9p3b0=l0c',V1JCgi#%pV,qU2YЍdHԉلX:fmb8+c s :5cT; (0g <!*,8)9B-<\ ;6jmNÙd]BJ^ƨ9spH}x(‰p VVÞK&?7,zR-Yݫyɽ߀7#lnIfMMo\s\T5vzͺej%6vj%6{z Z7`]t[ҼtDJ=qˣY;oӝVvx~n[=h ד)6yӡ|u;,a5mCtΝc/fs-Nu1cԯ'{w(pȕ^L?ngFuO>h@Nϧ83 W:cg*.?b{sgY%Z'3Z)f*q'F7@!-]|]+fxW\RrPh3q*vbw$} nH6ÃhPдڄK7HO4!fP~6`A1@~[j*ݚG {|sk%I2 B_Ce<> C/UuFQK4S)iɭ@ f{Go((A)kDv#{<3i"4K-3DP6C:c2Ea=ݧӐj?;Ĕ:ECo4i[}?KvpS!fVnz|߷t2ޒAYaߟ}KǚƺGѭW  c{%@?k;ט"pG^rHP?HíAIK`("+[+jDK ]6i6`fK1:OAV Gg ~agUh_K׵qih#D}$1÷]e97" 5+"|<~~jh 8G+))6!n\=?3X1F*8T.qheP'JN ӜcilER(wk^Xߧ/૘oJ e1$!0 \1J4Wh~[ZǷtVpgb(yh*P*4:ni=pݡ"tB8Zg0Mg 5"N>{yܞҍL<^TI4|!;JZ~hO9~Ye,z\.Y5_竺\Oh|^ʳgU|z\ (ҋiOy|\ްSmc6-B(iݯٲL$zfe⁸6="_^M]o&ѯa\Y2]jv/z=GOgM6zY4QG/.(*ZFe>G먊h]GF_s8jެɻ/[\>\K$=\Cpn'.DŽ#$~y8}{zzsŦH;Q=W!x }$x o:[D <-Zʮ*".b:ˢ28M%++[C3께7^zD/[t9pa>6{6 lv 6K*].L=K4 1އ-pwOoJG Æ7.5K 2pN[o?̆M5W_=} (za4*nC\O>{Ka.s8'EAŁɝO> ~5 *44 n@)Ho?E?R\Rc[R |~YNC/JMZDͼHȯ0#HEVU]N\n dTy@F,pμ\gcr;g'x{p}Gbt+4|u@ϒd`]^b} sar˝h9/38{U죈ܜPG7vj<ӏV2V}|\*b^u B|DAy Yka>d]̩AXL %W̝lW>LeOMš}8 ~V~-0=(z\a=O@H`!_ԗhQ4|a]rG]O]cYՖv +ɚ"krwYcqX\ĕ=qCĵwwO:7NO.:q,3ukYӍM NX,Za!Šl,V)55H+.b,V)m!ªqRPV^=רmyo^YeO7 X4oh1M'|\bm!2+l]7˦. +7:ѨѼٛX.鐶3!u[:1 M[$D/^ɥ\K%{y/q}^Kuvvn|'O> r{Ϡ X1{cc# cc=+z_谆XyZ!lZA=G?1\O"P UoG̻ kB 6զU,l (H@Z.zjcevuY\#u3(iR@]8-rbNX F<;oZǨ ;l6 苴N>ܻX^PAlxj#" Poێ}_3۲S`zUPPkpyHup \GnJ 4>]!11z3#ד#fi5G54PpWZÜީ'΀{Zgu 5Y}4 q| nKn1g!I=OMb"Scabiw Ѝxdzha{ALSVu4 -*X4nΪӏ61uQv՗+3Q9?lٹ[MEu^sHWt˜$hNѱαW@} %SnspSkoępPOo8k`` ~(/&*Ԧ"_BrswUU}9H endstream endobj 201 0 obj << /Type /XRef /Index [0 202] /Size 202 /W [1 3 1] /Root 199 0 R /Info 200 0 R /ID [ ] /Length 519 /Filter /FlateDecode >> stream xӹOUAs" O* >| 񁊨$VXho/ cH, &&`abV2|rΝ;]A$ʳR@B(2IPhf b>(mpڹU418U&|P UP 5<צ[kP)}k&7-*> 5 Q8*mY8t 8 4tl}}QYsЫ2_ag_%ч9^7A a*eqIR-}|"̨ W*\pf&̩,e U܇K'n׌3h$8S#Fs62id8{#FaIOb'Bkh$H8zʠUL{C*zUUW5y^V_N-5y-?|ꋬ|~(|}6+tin 2ddȐ5bgTvYc endstream endobj startxref 259910 %%EOF shazam/inst/doc/Baseline-Vignette.R0000644000176200001440000001574014506573156016732 0ustar liggesusers## ----eval=TRUE, warning=FALSE, message=FALSE---------------------------------- # Import required packages library(alakazam) library(shazam) # Load and subset example data (for faster demonstration) data(ExampleDb, package="alakazam") ExampleDb <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG")) ## ----eval=TRUE, warning=FALSE, results="hide"--------------------------------- # Collapse clonal groups into single sequences clones <- collapseClones(ExampleDb, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) ## ----eval=F, warning=F, results="hide"---------------------------------------- # # Subset to sequences with clone_id=3170 # db_3170 <- subset(ExampleDb, clone_id == 3170) # dim(db_3170) # colnames(db_3170) # # # Generate a ChangeoClone object for lineage construction # clone_3170 <- makeChangeoClone(db_3170, seq="sequence_alignment", germ="germline_alignment") # # # Run the lineage reconstruction # dnapars_exec <- "/usr/local/bin/dnapars" # graph_3170 <- buildPhylipLineage(clone_3170, dnapars_exec, rm_temp=TRUE) # # # Generating a data.frame from the lineage tree graph object, # # and merge it with clone data.frame # graph_3170_df <- makeGraphDf(graph_3170, clone_3170) # dim(graph_3170_df) # colnames(graph_3170_df) ## ----eval=TRUE, warning=FALSE, results="hide"--------------------------------- # Count observed mutations and append mu_count columns to the output observed <- observedMutations(clones, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", regionDefinition=IMGT_V, nproc=1) # Count expected mutations and append mu_exptected columns to the output expected <- expectedMutations(observed, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", targetingModel=HH_S5F, regionDefinition=IMGT_V, nproc=1) ## ----eval=TRUE, warning=FALSE, results="hide"--------------------------------- # Calculate selection scores using the output from expectedMutations baseline <- calcBaseline(expected, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ## ----eval=FALSE, warning=FALSE, results="hide"-------------------------------- # # Calculate selection scores from scratch # baseline <- calcBaseline(clones, testStatistic="focused", # regionDefinition=IMGT_V, nproc=1) ## ----eval=FALSE, warning=FALSE, results="hide"-------------------------------- # # Calculate selection on charge class with the mouse 5-mer model # baseline_mk_rs5nf <- calcBaseline(clones, testStatistic="focused", # regionDefinition=IMGT_V, # targetingModel=MK_RS5NF, # mutationDefinition=CHARGE_MUTATIONS, # nproc=1) ## ----eval=TRUE, warning=FALSE, results="hide"--------------------------------- # Combine selection scores by time-point grouped_1 <- groupBaseline(baseline, groupBy="sample_id") ## ----eval=TRUE, warning=FALSE, results="hide"--------------------------------- # Subset the original data to switched isotypes db_sub <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) # Collapse clonal groups into single sequence clones_sub <- collapseClones(db_sub, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) # Calculate selection scores from scratch baseline_sub <- calcBaseline(clones_sub, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) # Combine selection scores by time-point and isotype grouped_2 <- groupBaseline(baseline_sub, groupBy=c("sample_id", "c_call")) ## ----eval=FALSE, warning=FALSE, results="hide"-------------------------------- # # First group by subject and status # subject_grouped <- groupBaseline(baseline, groupBy=c("status", "subject")) # # # Then group the output by status # status_grouped <- groupBaseline(subject_grouped, groupBy="status") ## ----eval=TRUE---------------------------------------------------------------- testBaseline(grouped_1, groupBy="sample_id") ## ----eval=TRUE, warning=FALSE------------------------------------------------- # Set sample and isotype colors sample_colors <- c("-1h"="seagreen", "+7d"="steelblue") isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") # Plot mean and confidence interval by time-point plotBaselineSummary(grouped_1, "sample_id") # Plot selection scores by time-point and isotype for only CDR plotBaselineSummary(grouped_2, "sample_id", "c_call", groupColors=isotype_colors, subsetRegions="cdr") # Group by CDR/FWR and facet by isotype plotBaselineSummary(grouped_2, "sample_id", "c_call", facetBy="group") ## ----eval=TRUE, warning=FALSE------------------------------------------------- # Plot selection PDFs for a subset of the data plotBaselineDensity(grouped_2, "c_call", groupColumn="sample_id", colorElement="group", colorValues=sample_colors, sigmaLimits=c(-1, 1)) ## ----eval=FALSE, warning=FALSE, results="hide"-------------------------------- # # Get indices of rows corresponding to IGHA in the field "db" # # These are the same indices also in the matrices in the fileds "numbOfSeqs", # # "binomK", "binomN", "binomP", and "pdfs" # # In this example, there is one row of IGHA for each sample # dbIgMIndex <- which(grouped_2@db[["c_call"]] == "IGHG") # # grouped_2 <- editBaseline(grouped_2, "db", grouped_2@db[-dbIgMIndex, ]) # grouped_2 <- editBaseline(grouped_2, "numbOfSeqs", grouped_2@numbOfSeqs[-dbIgMIndex, ]) # grouped_2 <- editBaseline(grouped_2, "binomK", grouped_2@binomK[-dbIgMIndex, ]) # grouped_2 <- editBaseline(grouped_2, "binomN", grouped_2@binomN[-dbIgMIndex, ]) # grouped_2 <- editBaseline(grouped_2, "binomP", grouped_2@binomP[-dbIgMIndex, ]) # grouped_2 <- editBaseline(grouped_2, "pdfs", # lapply(grouped_2@pdfs, function(pdfs) {pdfs[-dbIgMIndex, ]} )) # # # The indices corresponding to IGHA are slightly different in the field "stats" # # In this example, there is one row of IGHA for each sample and for each region # grouped_2 <- editBaseline(grouped_2, "stats", # grouped_2@stats[grouped_2@stats[["c_call"]] != "IGHA", ]) shazam/inst/doc/Baseline-Vignette.Rmd0000644000176200001440000004140714367147774017262 0ustar liggesusers--- title: 'Shazam: Quantification of selection pressure' author: "Namita Gupta & Jason Anthony Vander Heiden & Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteIndexEntry{Selection quantification} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- BASELINe quantifies selection pressure by calculating the posterior probability density function (PDF) based on observed mutations compared to expected mutation rates derived from an underlying SHM targeting model. Selection is quantified via the following steps: 1. Calculate the selection scores for individual sequences. 2. Group by relevant fields for comparison and convolve individual selection PDFs. 4. Plot and compare selection scores of different groups of sequences. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. The example dataset consists of a subset of Ig sequencing data from an influenza vaccination study (Laserson and Vigneault et al., PNAS, 2014). The data include sequences from multiple time-points before and after the subject received an influenza vaccination. Quantifying selection requires the following fields (columns) to be present in the table: * `sequence_id` * `sequence_alignment` * `germline_alignment_d_mask` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(shazam) # Load and subset example data (for faster demonstration) data(ExampleDb, package="alakazam") ExampleDb <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG")) ``` ## Preprocessing Before starting the selection analysis, data need to be prepared in one of two ways: 1. Constructing clonal consensus sequences. 1. Incorporating lineage information. ### Constructing clonal consensus sequences Individual sequences within clonal groups are not, strictly speaking, independent events and it is generally appropriate to only analyze selection pressures on an effective sequence for each clonal group. The `collapseClones` function provides one strategy for generating an effective sequences for each clone. It reduces the input database to one row per clone and appends `clonal_sequence` and `clonal_germline` columns which contain the consensus sequences for each clone. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Collapse clonal groups into single sequences clones <- collapseClones(ExampleDb, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) ``` ### Incorporating lineage information For each clone, lineage information can be incorporated following these steps: ```{r eval=F, warning=F, results="hide"} # Subset to sequences with clone_id=3170 db_3170 <- subset(ExampleDb, clone_id == 3170) dim(db_3170) colnames(db_3170) # Generate a ChangeoClone object for lineage construction clone_3170 <- makeChangeoClone(db_3170, seq="sequence_alignment", germ="germline_alignment") # Run the lineage reconstruction dnapars_exec <- "/usr/local/bin/dnapars" graph_3170 <- buildPhylipLineage(clone_3170, dnapars_exec, rm_temp=TRUE) # Generating a data.frame from the lineage tree graph object, # and merge it with clone data.frame graph_3170_df <- makeGraphDf(graph_3170, clone_3170) dim(graph_3170_df) colnames(graph_3170_df) ``` `makeGraphDf` creates a `data.frame` with the column `parent_sequence`, which can be used to analyze mutations for each sequence relative to their `parent_sequence`. ## Calculate selection PDFs for individual sequences Selection scores are calculated with the `calcBaseline` function. This can be performed with a single call to `calcBaseline`, which performs all required steps. Alternatively, one can perform each step separately for greater control over the analysis parameters. ### Calculating selection in multiple steps Following construction of an effective sequence for each clone, the observed and expected mutation counts are calculated for each sequence in the `clonal_sequence` column relative to the `clonal_germline`. `observedMutations` is used to calculate the number of observed mutations and `expectedMutations` calculates the expected frequency of mutations. The underlying targeting model for calculating expectations can be specified using the `targetingModel` parameter. In the example below, the default `HH_S5F` is used. Column names for sequence and germline sequence may also be passed in as parameters if they differ from the Change-O defaults. Mutations are counted by these functions separately for complementarity determining (CDR) and framework (FWR) regions. The `regionDefinition` argument defines whether these regions are handled separately, and where the boundaries lie. There are several built-in region definitions in the `shazam` package, both dependent upon the V segment being IMGT-gapped: * `IMGT_V`: All regions in the V segment, excluding CDR3, grouped as either CDR or FWR. * `IMGT_V_BY_REGIONS`: The CDR1, CDR2, FWR1, FWR and FWR3 regions in the V segment (no CDR3) treated as individual regions. * `IMGT_VDJ`: All regions, including CDR3 and FWR4, grouped as either CDR or FWR. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. * `IMGT_VDJ_BY_REGIONS`: CDR1, CDR2, CDR3, FWR1, FWR, FWR3 and FWR4 regions treated as individual regions. This `RegionDefinition` is initially empty, and one is created on the fly for each set of clonally related sequences. Users may define other region sets and boundaries by creating a custom `RegionDefinition` object. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Count observed mutations and append mu_count columns to the output observed <- observedMutations(clones, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", regionDefinition=IMGT_V, nproc=1) # Count expected mutations and append mu_exptected columns to the output expected <- expectedMutations(observed, sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", targetingModel=HH_S5F, regionDefinition=IMGT_V, nproc=1) ``` The counts of observed and expected mutations can be combined to test for selection using `calcBaseline`. The statistical framework used to test for selection based on mutation counts can be specified using the `testStatistic` parameter. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Calculate selection scores using the output from expectedMutations baseline <- calcBaseline(expected, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ### Calculating selection in one step It is not required for `observedMutation` and `expectedMutations` to be run prior to `calcBaseline`. If the output of these two steps does not appear in the input data.frame, then `calcBaseline` will call the appropriate functions prior to calculating selection scores. ```{r, eval=FALSE, warning=FALSE, results="hide"} # Calculate selection scores from scratch baseline <- calcBaseline(clones, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) ``` ### Using alternative mutation definitions and models The default behavior of `observedMutations` and `expectedMutations`, and by extension `calcBaseline`, is to define a replacement mutation in the usual way - any change in the amino acid of a codon is considered a replacement mutation. However, these functions have a `mutationDefinition` argument which allows these definitions to be changed by providing a `MutationDefinition` object that contains alternative replacement and silent criteria. `shazam` provides the following built-in `MutationDefinition` objects: * `CHARGE_MUTATIONS`: Amino acid mutations are defined by changes in side chain charge class. * `HYDROPATHY_MUTATIONS`: Amino acid mutations are defined by changes in side chain hydrophobicity class. * `POLARITY_MUTATIONS`: Amino acid mutations are defined by changes in side chain polarity class. * `VOLUME_MUTATIONS`: Amino acid mutations are defined by changes in side chain volume class. The default behavior of `expectedMutations` is to use the human 5-mer mutation model, `HH_S5F`. Alternative SHM targeting models can be provided using the `targetingModel` argument. ```{r, eval=FALSE, warning=FALSE, results="hide"} # Calculate selection on charge class with the mouse 5-mer model baseline_mk_rs5nf <- calcBaseline(clones, testStatistic="focused", regionDefinition=IMGT_V, targetingModel=MK_RS5NF, mutationDefinition=CHARGE_MUTATIONS, nproc=1) ``` ## Group and convolve individual selection distributions To compare the selection scores of groups of sequences, the sequences must be convolved into a single PDF representing each group. In the example dataset, the `sample_id` field corresponds to samples taken at different time points before and after an influenza vaccination and the `c_call` field specifies the isotype of the sequence. The `groupBaseline` function convolves the BASELINe PDFs of individual sequences/clones to get a combined PDF. The field(s) by which to group the sequences are specified with the `groupBy` parameter. The `groupBaseline` function automatically calls `summarizeBaseline` to generate summary statistics based on the requested groupings, and populates the `stats` slot of the input `Baseline` object with the number of sequences with observed mutations for each region, mean selection scores, 95% confidence intervals, and p-values with positive signs indicating the presence of positive selection and/or p-values with negative signs indicating the presence of negative selection. The magnitudes of the p-values (without the signs) should be interpreted as analogous to a t-test. ### Grouping by a single annotation The following example generates a single selection PDF for each unique annotation in the `sample_id` column. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Combine selection scores by time-point grouped_1 <- groupBaseline(baseline, groupBy="sample_id") ``` ### Subsetting and grouping by multiple annotations Grouping by multiple annotations follows the sample procedure as a single annotation by simply adding columns to the `groupBy` argument. Subsetting the data can be performed before or after generating selection PDFs via `calcBaseline`. However, note that subsetting may impact the clonal representative sequences generated by `collapseClones`. In the following example, subsetting precedes the collapsing of clonal groups. ```{r, eval=TRUE, warning=FALSE, results="hide"} # Subset the original data to switched isotypes db_sub <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) # Collapse clonal groups into single sequence clones_sub <- collapseClones(db_sub, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", regionDefinition=IMGT_V, method="thresholdedFreq", minimumFrequency=0.6, includeAmbiguous=FALSE, breakTiesStochastic=FALSE, nproc=1) # Calculate selection scores from scratch baseline_sub <- calcBaseline(clones_sub, testStatistic="focused", regionDefinition=IMGT_V, nproc=1) # Combine selection scores by time-point and isotype grouped_2 <- groupBaseline(baseline_sub, groupBy=c("sample_id", "c_call")) ``` ### Convolving variables at multiple levels To make selection comparisons using two levels of variables, you would need two iterations of groupings, where the first iteration of `groupBaseline` groups on both variables, and the second iteration groups on the "outer" variable. For example, if a data set has both case and control subjects annotated in `status` and `subject` columns, then generating convolved PDFs for each status would be performed as: ```{r, eval=FALSE, warning=FALSE, results="hide"} # First group by subject and status subject_grouped <- groupBaseline(baseline, groupBy=c("status", "subject")) # Then group the output by status status_grouped <- groupBaseline(subject_grouped, groupBy="status") ``` ### Testing the difference in selection PDFs between groups The `testBaseline` function will perform significance testing between two grouped BASELINe PDFs, by region, and return a data.frame with the following information: * `region`: The sequence region, such as `cdr` and `fwr`. * `test`: The name of the two groups compared. * `pvalue`: Two-sided p-value for the comparison. * `fdr`: FDR corrected p-value. ```{r, eval=TRUE} testBaseline(grouped_1, groupBy="sample_id") ``` ## Plot and compare selection scores for groups `plotBaselineSummary` plots the mean and confidence interval of selection scores for the given groups. The `idColumn` argument specifies the field that contains identifiers of the groups of sequences. If there is a secondary field by which the sequences are grouped, this can be specified using the `groupColumn`. This secondary grouping can have a user-defined color palette passed into `groupColors` or can be separated into facets by setting the `facetBy="group"`. The `subsetRegions` argument can be used to visualize selection of specific regions. Several examples utilizing these different parameters are provided below. ```{r, eval=TRUE, warning=FALSE} # Set sample and isotype colors sample_colors <- c("-1h"="seagreen", "+7d"="steelblue") isotype_colors <- c("IGHM"="darkorchid", "IGHD"="firebrick", "IGHG"="seagreen", "IGHA"="steelblue") # Plot mean and confidence interval by time-point plotBaselineSummary(grouped_1, "sample_id") # Plot selection scores by time-point and isotype for only CDR plotBaselineSummary(grouped_2, "sample_id", "c_call", groupColors=isotype_colors, subsetRegions="cdr") # Group by CDR/FWR and facet by isotype plotBaselineSummary(grouped_2, "sample_id", "c_call", facetBy="group") ``` `plotBaselineDensity` plots the full `Baseline` PDF of selection scores for the given groups. The parameters are the same as those for `plotBaselineSummary`. However, rather than plotting the mean and confidence interval, the full density function is shown. ```{r, eval=TRUE, warning=FALSE} # Plot selection PDFs for a subset of the data plotBaselineDensity(grouped_2, "c_call", groupColumn="sample_id", colorElement="group", colorValues=sample_colors, sigmaLimits=c(-1, 1)) ``` ## Editing a field in a Baseline object If for any reason you need to edit the existing values in a field in a `Baseline` object, you can do so via `editBaseline`. In the following example, we remove results related to IGHA in the relevant fields from `grouped_2`. When the input data is large and it takes a long time for `calcBaseline` to run, `editBaseline` could become useful when, for instance, you would like to exclude a certain sample or isotype, but would rather not re-run `calcBaseline` after removing that sample or isotype from the original input data. ```{r, eval=FALSE, warning=FALSE, results="hide"} # Get indices of rows corresponding to IGHA in the field "db" # These are the same indices also in the matrices in the fileds "numbOfSeqs", # "binomK", "binomN", "binomP", and "pdfs" # In this example, there is one row of IGHA for each sample dbIgMIndex <- which(grouped_2@db[["c_call"]] == "IGHG") grouped_2 <- editBaseline(grouped_2, "db", grouped_2@db[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "numbOfSeqs", grouped_2@numbOfSeqs[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomK", grouped_2@binomK[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomN", grouped_2@binomN[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "binomP", grouped_2@binomP[-dbIgMIndex, ]) grouped_2 <- editBaseline(grouped_2, "pdfs", lapply(grouped_2@pdfs, function(pdfs) {pdfs[-dbIgMIndex, ]} )) # The indices corresponding to IGHA are slightly different in the field "stats" # In this example, there is one row of IGHA for each sample and for each region grouped_2 <- editBaseline(grouped_2, "stats", grouped_2@stats[grouped_2@stats[["c_call"]] != "IGHA", ]) ``` shazam/inst/doc/Targeting-Vignette.Rmd0000644000176200001440000001773514367147774017473 0ustar liggesusers--- title: 'Shazam: Inferring SHM targeting models' author: "Namita Gupta & Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4.5 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4.5 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{SHM targeting models} %\usepackage[utf8]{inputenc} --- The targeting model is the background likelihood of a particular mutation, based on the surrounding sequence context as well as the mutation itself. The model is inferred from observed mutations in the data. The model can then be transformed into a distance function to compare Ig sequences of a given dataset based on the likelihood of the observed mutations. This is done via the following steps: 1. Infer a substitution model, which is the likelihood of a base mutating to each other base given the microsequence context. 2. Infer a mutability model, which is likelihood of a given base being mutated given the microsequence context and substitution model. 3. Visualize the mutability model to identify hot and cold spots. 4. Calculate a nucleotide distance matrix based on the underlying SHM models. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. Inferring a targeting model requires the following fields (columns) to be present in the table: * `sequence_id` * `sequence_alignment` * `germline_alignment_d_mask` * `v_call` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(shazam) # Load example data data(ExampleDb, package="alakazam") # Subset to IGHG for faster usage demonstration db <- subset(ExampleDb, c_call == "IGHG") ``` ## Infer targeting model (substitution and mutability) The function for inferring substitution rates (`createSubstitutionMatrix`) counts the number of mutations from a given base to all others occurring in the center position for all 5-mer motifs in the dataset. The `model` argument of `createSubstitutionMatrix` allows the user to specify whether to count all mutations, or just silent mutations to infer the model. Column names for the sample sequence, germline sequence, and V call can also be passed in as parameters if they differ from Change-O defaults. Additionally, the `multipleMutation` parameter determines handling of multiple mutations in a single 5-mer: `independent` treats each mutation independently and `ignore` entirely disregards 5-mers with multiple mutations. ```{r, eval=FALSE} # Create substitution model using silent mutations sub_model <- createSubstitutionMatrix(db, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") ``` The function for inferring a mutability model (`createMutabilityMatrix`) counts the number of mutations in all 5-mer motifs of the dataset, and depends upon the inferred substitution rates. Similar parameters as those available for inferring the substitution rates are available to adjust this function. ```{r, eval=FALSE} # Create mutability model using silent mutations mut_model <- createMutabilityMatrix(db, sub_model, model="s", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", vCallColumn="v_call") ``` `createMutabilityMatrix` creates an object of class `MutabilityModel` that contains a named numeric vector of 1024 normalized mutability. The numbers of silent and replacement mutations used for estimating the 5-mer mutabilities are recorded in the `numMutS` and `numMutR` slots, respectively. rates. The `source` slot contains a named vector indicating whether each 5-mer mutability was inferred or measured. A data.frame with both the mutability values and derivation source. ```{r, eval=FALSE} # Number of silent mutations used for estimating 5-mer mutabilities mut_model@numMutS # Number of replacement mutations used for estimating 5-mer mutabilities mut_model@numMutR # Mutability and source as a data.frame head(as.data.frame(mut_model)) ``` The inferred substitution and mutability models returned by the above functions only account for unambiguous 5-mers. However, there may be cases in which the user may need the likelihood of a mutation in a 5-mer with ambiguous characters. Each of the above functions has a corresponding function (`extendSubstitutionMatrix` and `extendMutabilityMatrix`) to extend the models to infer 5-mers with Ns by averaging over all corresponding unambiguous 5-mers. ```{r, eval=FALSE} # Extend models to include ambiguous 5-mers sub_model <- extendSubstitutionMatrix(sub_model) mut_model <- extendMutabilityMatrix(mut_model) ``` These extended substitution and mutability models can be used to create an overall SHM targeting matrix (`createTargetingMatrix`), which is the combined probability of mutability and substitution. ```{r, eval=FALSE} # Create targeting model matrix from substitution and mutability models tar_matrix <- createTargetingMatrix(sub_model, mut_model) ``` All of the above steps can be combined by using the single function `createTargetingModel` to infer a `TargetingModel` object directly from the dataset. Again, the numbers of silent and replacement mutations used for estimating the 5-mer mutabilities are also recorded in the `numMutS` and `numMutR` slots respectively. Additionally, it is generally appropriate to consider the mutations within a clone only once. Consensus sequences for each clone can be generated using the `collapseClones` function. ```{r, eval=TRUE, warning=FALSE} # Collapse sequences into clonal consensus clone_db <- collapseClones(db, cloneColumn="clone_id", sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", nproc=1) # Create targeting model in one step using only silent mutations # Use consensus sequence input and germline columns model <- createTargetingModel(clone_db, model="s", sequenceColumn="clonal_sequence", germlineColumn="clonal_germline", vCallColumn="v_call") ``` ## Visualize targeting model The visualization of a dataset's underlying SHM mutability model can be used to investigate hot and cold spot motifs. The length of the bars on the plot of mutability rates corresponds to the likelihood of a given base in the given 5-mer being mutated. The plotting function `plotMutability` has an argument `style` to specify either a hedgehog plot (circlular) or barplot diplay of 5-mer mutability rates. If the mutability for only specific bases is required, this can be specified via the `nucleotides` argument. ```{r, eval=TRUE, warning=FALSE, fig.width=7, fig.height=7.5} # Generate hedgehog plot of mutability model plotMutability(model, nucleotides="A", style="hedgehog") plotMutability(model, nucleotides="C", style="hedgehog") ``` ```{r, eval=TRUE, warning=FALSE, fig.width=7, fig.height=4.5} # Generate bar plot of mutability model plotMutability(model, nucleotides="G", style="bar") plotMutability(model, nucleotides="T", style="bar") ``` ## Calculate targeting distance matrix In the Change-O pipeline, the `hs5f` cloning method rely on an inferred targeting model. If users wish to use a targeting model inferred from their data to assign distance between sequences for clonal grouping, then the observed SHM targeting rates must be transformed into distances. The `calcTargetingDistance` function returns a matrix of distances between each 5-mer and each corresponding mutation of the center base. This matrix can also be generated and written directly to a file using the function `writeTargetingDistance`. ```{r, eval=TRUE, warning=FALSE} # Calculate distance matrix dist <- calcTargetingDistance(model) ``` shazam/inst/doc/DistToNearest-Vignette.R0000644000176200001440000001340114506573166017731 0ustar liggesusers## ----eval=TRUE, warning=FALSE, message=FALSE---------------------------------- # Import required packages library(alakazam) library(dplyr) library(ggplot2) library(shazam) # Load and subset example data (for speed) data(ExampleDb, package="alakazam") set.seed(112) db <- ExampleDb %>% sample_n(size=500) db %>% count(sample_id) ## ----eval=TRUE, warning=FALSE------------------------------------------------- # Use nucleotide Hamming distance and normalize by junction length dist_ham <- distToNearest(db %>% filter(sample_id == "+7d"), sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # Use genotyped V assignments, a 5-mer model and no normalization dist_s5f <- distToNearest(db %>% filter(sample_id == "+7d"), sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="hh_s5f", normalize="none", nproc=1) ## ----eval=FALSE, warning=FALSE------------------------------------------------ # # Single-cell mode # # Group cells in a one-stage process (VJthenLen=FALSE) and using # # both heavy and light chain sequences (onlyHeavy=FALSE) # # data(Example10x, package="alakazam") # dist_sc <- distToNearest(Example10x, cellIdColumn="cell_id", locusColumn="locus", # VJthenLen=FALSE, onlyHeavy=FALSE) ## ----eval=TRUE, warning=FALSE, fig.width=7------------------------------------ # Generate Hamming distance histogram p1 <- ggplot(subset(dist_ham, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + labs(x = "Hamming distance", y = "Count") + scale_x_continuous(breaks=seq(0, 1, 0.1)) + theme_bw() plot(p1) ## ----eval=TRUE, warning=FALSE, fig.width=7------------------------------------ # Generate HH_S5F distance histogram p2 <- ggplot(subset(dist_s5f, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=1) + geom_vline(xintercept=7, color="firebrick", linetype=2) + labs(x = "HH_S5F distance", y = "Count") + scale_x_continuous(breaks=seq(0, 50, 5)) + theme_bw() plot(p2) ## ----eval=TRUE, warning=FALSE, fig.width=7------------------------------------ # Find threshold using density method output <- findThreshold(dist_ham$dist_nearest, method="density") threshold <- output@threshold # Plot distance histogram, density estimate and optimum threshold plot(output, title="Density Method") # Print threshold print(output) ## ----eval=TRUE, warning=FALSE, fig.width=7------------------------------------ # Find threshold using gmm method output <- findThreshold(dist_ham$dist_nearest, method="gmm", model="gamma-gamma") # Plot distance histogram, Gaussian fits, and optimum threshold plot(output, binwidth=0.02, title="GMM Method: gamma-gamma") # Print threshold print(output) ## ----fields, eval=TRUE, warning=FALSE----------------------------------------- dist_fields <- distToNearest(db, model="ham", normalize="len", fields="sample_id", nproc=1) ## ----eval=TRUE, warning=FALSE, fig.width=7------------------------------------ # Generate grouped histograms p4 <- ggplot(subset(dist_fields, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + labs(x = "Grouped Hamming distance", y = "Count") + facet_grid(sample_id ~ ., scales="free_y") + theme_bw() plot(p4) ## ----cross, eval=TRUE, warning=FALSE------------------------------------------ dist_cross <- distToNearest(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, normalize="len", cross="sample_id", nproc=1) ## ----eval=TRUE, warning=FALSE, fig.width=7------------------------------------ # Generate cross sample histograms p5 <- ggplot(subset(dist_cross, !is.na(cross_dist_nearest)), aes(x=cross_dist_nearest)) + labs(x = "Cross-sample Hamming distance", y = "Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") + theme_bw() plot(p5) ## ----subsample, eval=TRUE, warning=FALSE-------------------------------------- # Explore V-J-junction length groups sizes to use subsample # Show the size of the largest groups top_10_sizes <- ExampleDb %>% group_by(junction_length) %>% # Group by junction length do(alakazam::groupGenes(., first=TRUE)) %>% # Group by V and J call mutate(GROUP_ID=paste(junction_length, vj_group, sep="_")) %>% # Create group ids ungroup() %>% group_by(GROUP_ID) %>% # Group by GROUP_ID distinct(junction) %>% # Count unique junctions per group summarize(SIZE=n()) %>% # Get the size of the group arrange(desc(SIZE)) %>% # Sort by decreasing size select(SIZE) %>% top_n(10) # Filter to the top 10 top_10_sizes # Use 30 to subsample # NOTE: This is a toy example. Subsampling to 30 sequence with real data is unwise dist <- distToNearest(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, normalize="len", subsample=30) shazam/inst/doc/Shmulate-Vignette.R0000644000176200001440000000403614506573205016761 0ustar liggesusers## ----eval=TRUE, warning=FALSE, message=FALSE---------------------------------- # Import required packages library(shazam) # Input sequence sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATA.TTTA" # Simulate introduction of 6 mutations using the default HH_S5F targeting model shmulateSeq(sequence, numMutations=6) # Simulate introduction of mutations at frequency 0.2 using the default HH_S5F targeting model shmulateSeq(sequence, numMutations=0.2, frequency=TRUE) # Simulate introduction of 4 mutations using the MK_RS5NF targeting model shmulateSeq(sequence, numMutations=4, targetingModel=MK_RS5NF) ## ----eval=TRUE, warning=FALSE, message=FALSE---------------------------------- # Import required packages library(alakazam) library(igraph) library(shazam) # Load example lineage data(ExampleTrees, package="alakazam") graph <- ExampleTrees[[17]] # Input sequence to be used as MRCA of the lineage tree sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATAGTTTA" # Simulate using the default HH_S5F targeting model shmulateTree(sequence, graph) ## ----eval=TRUE, warning=FALSE------------------------------------------------- # The annotation field called "sample_id" vertex_attr(graph)$sample_id # notice that node "GN5SHBT01AKANC" is an offspring of "Inferred1" par(mar=c(0, 0, 0, 0) + 0.1) plot(graph, layout=layout_as_tree, edge.arrow.mode=0, vertex.label.cex=0.75) # Exclude nodes without a sample identifier # The nodes "Germline" and "Inferred1" are thus excluded # As a corollary, "GN5SHBT01AKANC", the offspring of "Inferred1", is also excluded # In this case, "GN5SHBT07JDYW5" is then taken to be the MRCA shmulateTree(sequence, graph, field="sample_id", exclude=NA) ## ----eval=TRUE, warning=FALSE------------------------------------------------- # The "Inferred1" node is taken to be the MRCA and has 2 immediate offsprings par(mar=c(0, 0, 0, 0) + 0.1) plot(graph, layout=layout_as_tree, edge.arrow.mode=0, vertex.label.cex=0.75) # Add 20% mutation rate to the immediate offsprings of the MRCA shmulateTree(sequence, graph, junctionWeight=0.2) shazam/inst/doc/Shmulate-Vignette.Rmd0000644000176200001440000001440714367147774017322 0ustar liggesusers--- title: 'Shazam: Simulating sequence mutations' author: "Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 5 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 5 fig_width: 7.5 highlight: pygments theme: readable toc: yes md_document: fig_height: 5 fig_width: 7.5 preserve_yaml: no toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Simulating sequence mutations} %\usepackage[utf8]{inputenc} --- `SHazaM` provides two functions for simulating mutated sequences, one at the sequence level (`shmulateSeq`), and the other at the lineage level (`shmulateTree`). Both functions rely on a 5-mer targeting model for computing the probabilities of mutations at each position along the input sequence. The 5-mer targeting models currently availbale in `SHazaM` are: - `HH_S5F`: Human Heavy chain, Silent, 5-mer, Functional targeting model - `HKL_S5F`: Human Kappa and Lambda light chain, Silent, 5-mer, Functional targeting model - `MK_RS5NF`: Mouse Kappa light chain, Replacement and Silent, 5-mer, Non-Functional targeting model - `U5N`: Uniform 5-mer Null targeting model ## Simulate mutations in a single sequence `shmulateSeq` generates random mutations in an input sequence. This sequence is provided by the user as a string, with the acceptable alphabet being `{A, T, G, C, N, .}`. Note that `-` is not accepted as part of the input sequence. If the input sequence has a non-triplet overhang at the end, it will be trimmed to the last codon. For example, `ATGCATGC` will be trimmed to `ATGCAT` before mutations are introduced. The total number or frequency of mutations to be introduced is user-specified via `numMutations` with `frequency` set to `FALSE` (default) or `TRUE` respectively. For `frequency=TRUE`, the number of mutations to be introduced is calculated as the length of the sequence multiplied by the specified mutation frequency and rounded down to the nearest whole number (`floor`). Mutations are not introduced to positions in the input sequence that contain `.` or `N`. Mutations are introduced iteratively using a targeting model. Targeting probabilities at each position are updated after each iteration. ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(shazam) # Input sequence sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATA.TTTA" # Simulate introduction of 6 mutations using the default HH_S5F targeting model shmulateSeq(sequence, numMutations=6) # Simulate introduction of mutations at frequency 0.2 using the default HH_S5F targeting model shmulateSeq(sequence, numMutations=0.2, frequency=TRUE) # Simulate introduction of 4 mutations using the MK_RS5NF targeting model shmulateSeq(sequence, numMutations=4, targetingModel=MK_RS5NF) ``` ## Simulate mutations in a lineage tree `shmulateTree` generates a set of simulated sequences based on an input sequence and a lineage tree. The input sequence will act as the most recent common ancestor (MRCA) of the lineage tree, and sequences in the offspring nodes will be simulated with the numbers of mutations corresponding to the edge weights of the tree. The lineage tree is supplied by the user as an igraph `graph` object, such as that returned by `buildPhylipLineage` of the `alakazam` package. For details, see the `Reconstruction of Ig lineage trees` vignette of `alakazam`. It is assumed that the `name` vertex attribute of the root node is `Germline`, as is the case with the trees built by `buildPhylipLineage`. ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(igraph) library(shazam) # Load example lineage data(ExampleTrees, package="alakazam") graph <- ExampleTrees[[17]] # Input sequence to be used as MRCA of the lineage tree sequence <- "NGATCTGACGACACGGCCGTGTATTACTGTGCGAGAGATAGTTTA" # Simulate using the default HH_S5F targeting model shmulateTree(sequence, graph) ``` It is possible to exclude certain specified nodes from being considered as the MRCA and from being included as part of the simulation. To specify such nodes, use the `field` argument to indicate which annotation field in `vertex_attr(graph)` contains information relevant to deciding which nodes to exclude, and the `exclude` argument to indicate the value in the annotation field that nodes to be excluded carry. Note that when excluding some nodes, additional nodes that have not been explicitly specified by the user to be excluded may also get excluded. For example, suppose that node B is an offspring of node A; and node A has been specified by the user to be excluded. As a corollary of node A being excluded, its offspring node B will also become excluded, despite not being specified explicitly. ```{r, eval=TRUE, warning=FALSE} # The annotation field called "sample_id" vertex_attr(graph)$sample_id # notice that node "GN5SHBT01AKANC" is an offspring of "Inferred1" par(mar=c(0, 0, 0, 0) + 0.1) plot(graph, layout=layout_as_tree, edge.arrow.mode=0, vertex.label.cex=0.75) # Exclude nodes without a sample identifier # The nodes "Germline" and "Inferred1" are thus excluded # As a corollary, "GN5SHBT01AKANC", the offspring of "Inferred1", is also excluded # In this case, "GN5SHBT07JDYW5" is then taken to be the MRCA shmulateTree(sequence, graph, field="sample_id", exclude=NA) ``` It is also possible to add a proportional number of mutations to the immediate offsprings of the MRCA based on the fraction of the nucleotide sequence that is within the junction region. This is achieved via the optional `junctionWeight` argument, to be supplied as a numeric value between `0` and `1`. As an example, suppose that the MRCA has two immediate offsprings, each containing 2 and 4 mutations respectively compared to the MRCA. With `junctionWeight=0.2`, the number of mutations to be introduced to these two offsprings will become `round(2*(1+0.2))` (2) and `round(4*(1+0.2))` (5) respectively. ```{r, eval=TRUE, warning=FALSE} # The "Inferred1" node is taken to be the MRCA and has 2 immediate offsprings par(mar=c(0, 0, 0, 0) + 0.1) plot(graph, layout=layout_as_tree, edge.arrow.mode=0, vertex.label.cex=0.75) # Add 20% mutation rate to the immediate offsprings of the MRCA shmulateTree(sequence, graph, junctionWeight=0.2) ``` shazam/inst/doc/DistToNearest-Vignette.pdf0000644000176200001440000101025614506573170020302 0ustar liggesusers%PDF-1.5 % 12 0 obj << /Length 1633 /Filter /FlateDecode >> stream xXYF~WXʋGMݾx# PK B=v/|,>U]m=ZXXVyQU]W]=;yѓ"v~(YJE!K0@<_r` OČ'́卪Ap˱cث㨿<ٛRDUX?}hF^J%TY @x>x@jG)1!#R;D'}cx~kA,Cy#1YC ϪjK j - 9L{b xy2*H]! ejl,@P\`OL'9A dԧ+Zʐ頊=B!qF52Cﺏ7x ^˒X[Ut:'dv hJ+}/xi"L\qi?buUEmq Y*d\fAL A,\@è6a)Hӏ^=6R)hzxkzkӾk֮wj8n W]S5S/56&:M#.^{=^luLS[{uF8T'f4G Ny?-O! R\IfI7 jh&FL,SӐ[  -1q1d?DYA 4аͶT2ۮhgP#BvET4ҊJ"&}hF9LB| C\KK oF*s{4yDžMꡥ렉ABZ5`6)|̡U[A|W5/W&AtnĸM"RYOҩ6@Pa|Es\M5EOMb&L =ZYyS[k>j*¸hʲA/_6nk<;N&I! uQ#QScFY/Sx[kTpH3Ӕp@ K]n6S.uhhJf(Kf{ASfpWSn̵7R!8Mg*C&^8^!I9lG:c[:颵84Քb- 1ŽO ,΀,xuIR)o > i ܾRϜ}FgN{]悖ԝkoYTr֍x([VC&ϖ*R}WDM5kLABIBx,w#`Z7,tS5XѱbD+ Ӣ ljWuG+/ ͧwO PO}zG\˶n}zav~SY!>!cm @Ǽ/u02w?Nb+gFhd.:[lCU6@c9t~/bZ9n5UNuW'wT"/o8KxU}[6CT'{pze(pyI 8BUk[x]wZ"ab||?d endstream endobj 36 0 obj << /Length 2899 /Filter /FlateDecode >> stream xڽZ{o6?bѢ*"EQiZ\[8\K*c+i:Aj%Y^IQ87Cjv.~|gEl@8cF=#JKܛ;ߌf E8bQ/?3yg>}JD6.hmMJ€$Q< !kK] QQo_&sSoo7fmiɋꄺ 60YO(f]nN+Nc覓pEeDRܟ~FSw~ń_="tқ,ߜd h=z|~WݷwM ~|*AAttjz%v~Jpq"ȴD,Caڞ"m|;hmgmDKq7ܵFbgye4GA?H 8^ sRE2{ctWD2B'@i_ݾN/.8b=)e(Z^=juzP3(zNکǁe&D67<+_ t_$,cޡ1"9X )GKӶ:ngܽiUŎ7o{&[V-/jm;˚X22m+I]ƭY%xqMyK$3TiD|5J}NCȽ? k۷M7yG[te37)mg_t5u0 HyfFT]B#'#p>/xѲ=4vp"'~1F"00`I-TaX@ePڪP77{aޙN HՕm~wג"}jMy0{ &@͐eX.nms>( @*/V__.V 1 \\bfϥ yDo4Ǜ0R!#Vc\ůfc(pGX^7T P ݪA2v$i Mt)R[;\rxjȫuqMaACIO;hsĀ56p6z?/8Af]}x;ƾgpƂ Ck(F>]QH.Zq5gρd2jI10Ck(,&,Uٲ6\3+*>4 4 YW7.T/iMF$ RD8@ E]J̇JW]8HEh`2k=՟&zg0vF=ӱ*fՍ?N"6AtH=) ⩛yϷ [mħ&>dj Əy{?m*/͝W+7d3GBC~Kڏ[>B .?NOMSEzK"Je7Gk o*GCEq}GL0?mJ[ux H=pJl= n$:I3u'A endstream endobj 42 0 obj << /Length 3072 /Filter /FlateDecode >> stream xn:_atq3Ui>9}9]nǖ|pHI/j>,H FEQ4:}gN^%j#F)]\b1E fџb]^ˣ^y(EL)hG?'Z')H+_Nfb"ŋ}u,|t4ړ6.~/7WXg"ݕ'Oz4Ƴ;xǎ`=bNJW*Ae&\2 O_ϫbs]އ㯛rZ/r;0l<ϊ!<| o! >_(je>ǣp:+c0\Uӝ %KeCH!IGTkΑt(LOQ^tSd:XKdq5?||\.su+@OyQp-V$Na_6p.(&ƥ`)W!6Lՠ]25mF&%ٝ;g?;}{rv ~8f$-@)5ű5&孆lc"OPV'H5ͲXg Ŧg\bMMu$-gefUV)5-I,fvVE^]ѣe5ZEe“F9@nc:6x8AT7'E5pPDf‰T# h4c7xk..lEώݯ;2jgp1iLd=@k14Nio d{sF"@Gz6м}҈-t`oB_INKÒT6`9TDheاs$8ެ%A@ x ']Bm\ dW?'+=zcipq"eoA)!^!C|85nc/_r:>VNh7-#^ήQ0媺tLpDEX0(7\_pr^Zj,L=*lFЕRLA -"8p}U~Ya'=X %q:NI8 H#xDVU5p)xG;I!$Sh(0gϝkJlvЖ`L&=d/"z1DHH( wxA;dgL^o$kn ֣ O=ΊjY70Pf]exH7iRQ@4}0Ɠ 030%"mGo!2HB A-Boip| o|7D0$D 0iP4KDo H"HKѢ2?-:φ$1@'U,2[t^ EU$Vi= UHCu@GiL u7BT BbMj;;vm,<6BDi#o"&/&lⲷrE 7>(j~fȵLjOk]~P I9Z}rE-xVqbWXS,۪ɡ.KPUMe:]%RuXhfܠ*jxi35W"a*mZ]WVb$"bw%8uc(߀wN5k,h$kpMDĤPI𨇶7~)X;ߋVE1 d =LC2zˡD"& ִPCJaV**vUUpZ *P\g0{tZM[KL˯uwj`PN+a*i8y3¦P*_eHd:T"¤,? %YY,ǐڌs1dby4Nc'w:_Ȕcoʩ6elku!R՘20C ]mlZbܱDx4~!Kc|HFgCXb[b'-h1w:xT2~[= O_X+⵿zcQ _(vr]24n˸S_rl<\q?i J(SW6 Jw_lh#BrT4^q(-faWhe$}=@(w^^<. endstream endobj 49 0 obj << /Length 1720 /Filter /FlateDecode >> stream xXmo6_e_dfDQ/~Y ۼO6c DW/I_;eKXx${wG6V^]ś$8gYKC/ 2Dܛ-_M^&S~UDpc˼iU [ĺuWTo4*uʜv ֦X6g\G!<%A%R(JY gk S祪4'5zNlw, Xtjs3~E9rf{7Ռu Z6ȩK҈9cvEm3Jd$m ,AM9fY ӄ%<~+0 20Ci9T֪Zoį@D~;dP"3.r{DG Yb- HbKبZiwvachn>G& 8āR4@_S7z4ȧڔ_>ЭЈ.,AkiЛOg:4 kMjj\yK; *gŕDi7Y˔?ˁ"baL壈5̪V%C}.X&} U]@3NuYd_ߚW͆v/T$D,5'Οs$bk}ƂSs>FeTךk MH\_L B6&_ T9u(6l͇udr  fwC˄P V֍I 2/Լ/5$ ކ$|dc+K]5y}ě0LJ1j>4Zm CDXGWe,^kR]\_/w N!b[.v&W4qߩ^DlԎ!5iK#:UxdB7_h L JPLi'EdmVI*G0l?9*8 qky']ն4Yf&/7X;ol/wYJתNJta9m=#}5B \eOs]K70t5h*Ř St%Db+f$up/F<ޅ33> TPƃx6y]`dc+[^8>#' /GCRxQ4uva;(. ģދ>=]gv_smJg,'jx_%\SCfzJLdEP|g#K:lFo&ѹ GnJ ܳ07KB"Xg$`8lh0 #~j)L; m }T͊*_8upr$ΥdgxV{aĶk Oj l=8 V,NHU://5a_HD2vpMyu>W(o~y)> h!_a2ϫ|ٮUANp$,7>O=~y-73)A9X\絞Gl,'$jZωx~^jϿm\woW_Tg6Լ|Nl? ݧ추5[bm#v)m ˱k? 􅉷(>+Pƙ|ک~\+ڜzK)h?x^2CP[ endstream endobj 46 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-4-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 53 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 54 0 R>> /ExtGState << >>/ColorSpace << /sRGB 55 0 R >>>> /Length 972 /Filter /FlateDecode >> stream xVKo7 ϯб9)u!!p"Fw)K4#*Y9c0Z)$Cʠy0/{s Xk^/ \ߍ5o4{XЊiY[.?RbnK8!o!K!>e= 赤`$y$ќ <5xBFN)T+s M. !rK$JT 7Q)"p"CHE[]4Q+]4Qg1ZvCQjjiiBMVE'ʪUV I)*=*|0SJBsWCuWl(]VRwD瀺&jJB\ʭ\nDi\rl`L\374G`QBDG϶*tMz4*q 6:`?ES(!g$g2Mk>ٙy(gi(MՐ,'n5tJrd$xث˰/hwSc ư}+S1(|caN t9V_U<2`yHlJ qMJ (%Qd5#Q dBȎk>7CyIɖכ Iöˣm жmyIikƘgǘa, 6^)_۶@}W`o?\] NoU(LS_^>M5 endstream endobj 57 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 62 0 obj << /Length 1436 /Filter /FlateDecode >> stream xXmo6_e c1+QD+e]ذ@K$zyc:C;|#uGY:vt:`K|gz@sx9S'1xS:ܫ$[՘&n7nZ(YUO(wżeu#LBT6Ca%2-bW)ċ#Astq“Rjl Q E޴i57ڬM&\a3PM\3L 8N(x( K(¨DZPb tJ~Lcŧ\F `'qj--'K΄(ʜ|=4tߊJi+͟5IR zrY6=XH\cE Z! ZKhz&0!l$媐 B0`&f{fQt41ɒ<ܼ!Uz $Ѵp cFXl/9BH\X?ʮj/~ji!n>eU'6Ez׼>KوGV!֜|e h)nf}n!9f!> i+h+y98" a_n5^^/F~lwf﫾l.xU˜ &4f*YCNSc?ZJV:|X}eݘ n\'SpZYBPXbIo%PZ5yf|ǝa$b a>C(LsT35K|n1?zV-E)WŮ&syQ`@;&<̫TaWkȭ$N*gz%(uM)zZ$}5ؗLiW5ݢB_:2״ނf/RG!*_u-JxY~PQBǞQyUIǔքԅ}.c&x4#XPU!JmSK"?HL T: BĞ@v-@Ei9Bt욼Z21JÎ'٪6ܹcN(XGkxl_X5y֊ki[w%!64C}[hЕGJoG8{GO3O/=`7!~.B9&^­PMǮ/3//7i {hs2W$am!N7ۜHq!HL'!7߮C | endstream endobj 58 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-5-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 66 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 67 0 R>> /ExtGState << >>/ColorSpace << /sRGB 68 0 R >>>> /Length 1194 /Filter /FlateDecode >> stream xWMo7 ϯб=TAh@AQNhH33Fk=}pHo1`Oo~?-`sFO=]O?oyq > 8$gk/f; j&fG㓳g~1;K]pyoH.8/޶bKl&??Ӏ!x6A6-3aD(qw<,_=LIȄnJB<);[a' S٘MIhfeB7!\$b9LAa$D9) Yg|QS0E"ρ d?lo"3-s B-q?at78Ô+rT)ÎF~Ô,pT) %Q X󢛒10%&)0Y\ȑ鰶`b6R cZl ,lK( @th4n-qIW-[Wٰa%#Ur#@R(O6mY,%k3S)`=foC˩'j~z- @|k`(F|!q{|j~R;dh+9P@lPӖfh{LDY'\韯L˓wr oCeY^˓M!a̪^XЀuk c݇Ui~~J5@U3 YSB6b4?#6U~G ZE60F)I3qؤ3̭Cϯ /Kӡ;HH~ǼBme>۾I֡ϒ5lr_廻u'\Qۥ>]7RfOSӕzjyZshj)$@h*}O? oOJpW{lSxxv sz|VHfTEk%J7$pxzx|WU'5BY3b]|rcmr`޺۞߷ބ^^w<}s\?~a(Ю  endstream endobj 70 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 74 0 obj << /Length 1848 /Filter /FlateDecode >> stream xڵnF_Ax@x3HveG$"<﷪He[I282p'_ с$5w߉LQ6Wۚ0폍 0i El' zc~>swoߧYdh m}ՎǾ]]l~}iQJ Dz^vK[eDXe$i(KfJ@P#׸&Vd8wlSY#T=c *"٠F@؜5l7ʺb$q C=ʂp^t4z4y1owvm5e5Zl#SaZ2c_}8_LN][n b6ǫ_3zDq +vF#0+܀H'a%oFV*ݷ-J )(!5b>mO6s. ->mhxsM>̻H>%ͼ0sRP$jLK9F:7ѩ"W$t*[ _4%ȩt8zBZI(hE1d"LqPwV+2]4NDg$sGKH}>գn.*2cCקqժKZFV(erd*Q=b $0*,s9IQ 0!n (aD `wߝƪǬŁ,I,5X;QϒR^+Y oJȃT%`@=ta?ATx h.bOc`jw bXseE+cŗ|"LDb*Ă,AB̵N\jR*+ wc\͍,d^(3"бnU@oh:>P,80븋;Rׂmo2&UQOkkQ_J`jqBAE@9oN[PсҞA O.Ӥ[0>O#t_0͊8P4!j`GhHGt S[zI#\p5_'oT,~9#Ss15}s$Ի>S y\^tQ!x @:Icktw71?d\26,9e䁉&eSw;`zyKKL@eNYi&ƉrXDjh^gםOYa_3BZ.} oaꅰꅂ"} -._Wܒ(> /ExtGState << >>/ColorSpace << /sRGB 80 0 R >>>> /Length 2633 /Filter /FlateDecode >> stream x]9)<vo4)R#A"1h1@A"!ޞV}EBANzvٮgowMSMoo?\Kj+ב|Zl+uczw?YzVs)%==9*~#F_|;L}Tϛ[-萾ȣ%۹ZVwvӌnsfO]͵,ևf[ysgع'q銮0)+YCcDY^@rV| cjGUJ($|RrUpP'J®9[UnN$܆^]7*&S 4n2~܊L@,\P咓[:/RR$z:Y )Hl" 7/&SIMVBT$R]MQnBpS[Uj[Qn\ s/:on R9)F&dĆBd|Ey뒃$ayb-,M@.I2X8 "d#FPtDΗ*ىXqS5HN9bREE4-46Ҭ 2EVn2H3a$#+ 1EfRLo$$DXk KėIk'|l]fNŒ f c7.Ȉ/iV)kȰHFuJӚ]aQ) fTTi4LīƬXi4dJoL5ei2H5ApGrá-azC: Ci6(-Z,4+ J$i#d}1ѺYHf`/#SyT'ڕ9S~iEi64!K.>Yяk]#:eiZ4ͺjk,iVU)LhK?H*@3xmYfMfV'Νf2Buv|FɔR@%S:+ʨn#dJ5hIIS-/Yl]:*w Ni&S"f23LTFTS禯UhmH3jieJvb(o$dAAh]Ho#H3 f2heJkY#ҬQ<1ȮEf:90k\l 4[n)43CiJo#ͺ#KMʪAؾ༹<~!IN~&ͦEKou}:BuLC<?j9@J}9tחfR->6xмCwcW5o\y"Iˉ;o2 }i;Ҍκ+B.~l4IWbx84*v#͌2@uJ^FT;t -ȖXҬQZt^Ӕ%߶.-ҬPm4k.SSuG Ko+frحKo 9iھfRWD/*d?54㥷c>YC]zK\4jI3栙EwL9ScI3 I3& ->4s:yZAz]Ĉpfz4kjAF}酪L NTQj ِ)]iয96Yeӵv$f34uFFlדfeiJmK/'%;i;ҌY{>H6]RKҬ=J\Ҙ3&Ft%;1rԽ$F.Ч)_rD1S93/kߦSY Oj|g~_/O<{y==cυׯRM/Hf*I:_M?+?O/~Rv??=qdʑ1#Foq|KBGh*ޕE/|ȇv%{{ȫu}Ad'm-ST_58z4K6Ήv2G)%.*q Q8f</G-@8x竽zLO=̩~Ǧ ~qZPMwM?KSu5f0u/rWX endstream endobj 82 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 85 0 obj << /Length 1748 /Filter /FlateDecode >> stream xڽXY6~_n_ddDK)Р ! ڢe:\I~}l뵳kQsS8/_ ($XUW^(5/'y5o*\0b"(f' Z᯼kDϋDR`6E`gFmQ76M>.~ #LA; ]| sP/1 p=PW99M0'- @J!,8XT mӫxcaٜ{n&oeee*ַFp2^WbY ɞFcimmX O #eQY9, %pla1J^hT9qN}sJ;IJ/ClѼu= & ݶ, 1E`ͺ&0;@ gqXi,_6.΢[Δ=xA 5*)ogQr0ũ(cѹF*>`ƍpX5anՔCUwۙ-6pf}=$N_YjtIJBSEJ&a,u8޵<9ŋf PFbgJu(bkjtFO8Ndkc2<ymE}*3>8idPf,FG Wza3mi'(kY~Sq)\ "HnZqeM}P-J't`#8]uҶQ*,E^Զ=9 ۺ1v|yoEuBщ͕k{&%IAakR zIQebTE)"x8dsn Walr;U.dz$;D@K ұk&;Ky("=Le=:UPc`#hTvpFwzڪmƋau$u,tcTnax쌂h,yHqnYfMQ+?aQg޸ 4Sn\ߙq}Mb4]y@ ѷ-y{ޮC|v+V2&SD4,ފ7>Dh5 ܲIiJU_ "ӻ{?W/3 ( 5Q+9Xcyo.Wu}%=hn< XE8tFߧ)!ƕ=٤&ᅳo#@Qh=ᵪn)oa;~;ό|U<78#o4NNJȾA-oCEA$.t 7~Hy䖪;aoIC p>ro9H4US}piڧ»MѫIPb,juquڕO4 ;傰XZCC[UO]81a)朚DE endstream endobj 71 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-7-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 89 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 90 0 R>> /ExtGState << >>/ColorSpace << /sRGB 91 0 R >>>> /Length 3849 /Filter /FlateDecode >> stream x7ﯘ%,:kϘe[)HH,MU H$9^>S73cM^liao}uמ&eI{|-ql7nߌ~¿XV[:u:^容WZ ʣVzvܵ^ZeܓUCr=z=9^{i^ezˏ8Lr=Gt>ȵUڱسn]֎)y)KK\WR]֊W0λm<ԕ2ec8-")Rgd[V+Sv$dXƗĺ 9|6yq,fV+ICBgٸ5|6ه*>9ʼ69ʲZn!(ے9Ag^e[%d>gTDQ=Aj.Y|fW쫊D"gDP9}H+ʶp }F|H3"(GWHYe_\;#HDP }Fg$R}Sl1b/|Ɔ}F"(*Z)e=P;AgDDd3YgAg$e[2(/nS"ψe^OeEZ-|F"EAG2>#ψ3*ec,|F5}FQ0H),w>#ψ { d>EDQJyzb5%{)\e2eed>$R𹤾~!1֒%\Abm&//\sAQ}FQQ/\;AgE5ˀ2Ei33)Q >t33Je@AgEg$R>2 gDP}FQԢl ((|w>9| e@AQʭD VAEQ({, "(ʭ(>#Jg, )[}FQ}F"QJ, "([EQ}F2@P2@HDPY> 3ArK}$R#EYb (B Pn "(,(ςD e B (B%H3KYA%3D 喔gEg$2@AEg$R e( eA8}FEy|3 喠( ψ32@ADPEQA2@Z ψ3"(`kE[^Mcmej-5r5޸TgZMգƬq7b5D#WMu]hPJCcUSIUMbej4V5U[$PƪjOcUT|7V5%pdcS7y85l5ه*U-ǦXՒuxk sZ=r9Xɪ\Veh*DUBʐdUΛʆګŪWU9ʨdU***28YŪMV%2T7YŪdUU;YU[&:U?JAbU'2:YUSMVTUu;*ɪ&4V%-*gUr2V%OcUzfcU|3VeUd+cUrwVEjjr[cUz*cUrWƪpV5*9 cUD*J*I`UAZ0V%XYQ0VE0o RoYMVEѶɪ &;dw䬊oUBɪ$ۀ:`UIRaO5z*cUm8eYvXd;ɪ uPUIdUz*cUU;2VJ决*J톱*)6͜UAΪئ` nXYYX*¡kw*0' ʪDUQڙ5Xi7U| jU_X4VO*.vΪFP8}R{e2V5YJlR}6Vpn笊0VE4V%bAjd*&aJgULokUVEƪjZU}YxY+UQZ*jEGƪyi*JkUVŝZUY;YXXTm5VEX_kUVEX7kkUVEXdUQVŪΪFJM3gU(rVzoUQ6Y(NUI6UI6UQm*cZuSZkrKqUQm*ɶ~UbYK5AcUUN[0VE0V%UY${9Y(/Ub 5VEij^V;9Ϊ(Tƪ(Tƪ$Yl: ^ԛŶޠQ;Yը۝UQꗍU"Y7Yx"pVJMgUYY=NVE0V%r*͂O7' f^ }چdtalO/G1}z-WX_=[om׮}*6T8^?m8~zޏx#5==hۿwR endstream endobj 93 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 98 0 obj << /Length 1531 /Filter /FlateDecode >> stream xYo6_e/ O`}40 Xv0hÕylk[CE<~QҢ6߄:%̇7Jv)nGLFO_(hĬb, ! F@[oT̪˿&ziJ%~sƫjICA^+f-Ii#7't"tY&}DNY<?=iHNvxTV۾XRNiJfr:DD(Ӣ'zo&#KbVX<$=+FF$rB/ҋ;C԰ӗg=/F_4qyjNP X/ǜsJRkǢW Q6RV"#Le\9..RI} =<'q.bJʬ2* uvQG4ŵ4CE [cvbQ-*-@iLoQCחcev,}\Ѐu!Y.(LQ_DA|9)0^?; |H'omW->=CDҳAYw;  M]FM`۹@c&VRLżT6p ]XZ\ᬒ6RfMj$RB}<\yJǯPs7沝DP!&>o)TquJXjFY& ɥ$ѦPK =f'G}+YJN}Aó qyO+d{6'^{iQ]fh{@ O:~Urhw *r;J}J^1a@ w\4mKѦcPOۻ7͢S V;_"dyMcŇci7H N2/  靆4HϊLòp,Uk>4w_9%"M"p_a{ <E.x/ -(؁[,TvoUFnj]@9G|q+s :ɪpoe){~&ry|J|Mi Iٞ6WL "Dcj8Q4p?}1$wzG%9!f~.[ƻ ϿS["hh"-ApfI~Y'r74},ct sw4`rwSCyS5g+ m`qyd/H~ endstream endobj 94 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/tmp/RtmpS1FIqu/Rbuild17cc6339821fe/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-8-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 101 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 102 0 R>> /ExtGState << >>/ColorSpace << /sRGB 103 0 R >>>> /Length 1374 /Filter /FlateDecode >> stream xXKo7 ϯбEUϑtѦ PNqC=v7vjIQ^=J*EWLNӷ7JW;O4_JNZ~SO:htQhdsFF+1҇}vS=ztvL5W+6.~ooi9^$SD఺2e &~-O &ɟj j SBqϪtwV֧|ۇ6ꩃ\awgv(xwaGw'KJHPm얫K怘0 Y Vc+[s-rq;7Y}{b֠x "QɁ[> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 108 0 obj << /Length 1614 /Filter /FlateDecode >> stream xXmo6_!t( 6KRŲk mC[-4;(YT='n20 {LC#jFO0'p3[}>" DOvH=l1gyUfjFy|QЄ9KJ[匄4r|{x|pLFY+s7e1{Τȗ"S\q#J* s:)FzN2]oV+JK^ 'fzFuV-|u57-ʊ|:$L|@{L`BR9}:E|BmZ2pX@ {3e%nާ+J?L"@̢*+- ܅Y Lf[<4I+ŝ߇(0OݢVV(>*/L_$ȁ#:am0Aʋ9amҭhu䇔Ā%uϟGh؉lhu+aaƲG=lM#Ȃtk>9f ATeNڵP\ڡV-O|2̕!R~8" x.J/o:JeaO[ՍQ9OtŎl@uYC1S Z.b<쵝2V]' rPFmR~U&#qJuٜp5˽aɗͪ^lWt[āVQcHjbJx X6^,kk);ˎ,*6F'x@5Cp <4WS#Ј8[qOys*ejB&dgI{a2YM=AoɠLS,e3>R?FzmbU)@NE..;X)peq8]f{^л-Z2~d 챢Hy 8A0,+uځ(o{&-pq0bӷNIdG!/.nRr<1hL=g;G#n FAZ˷G/:7:>6e7z{ˋNVoYDǵ~}0ВnwK2ຓBvP0xο(ʮ)s1 c :MK (Zv4p0 u "> /ExtGState << >>/ColorSpace << /sRGB 113 0 R >>>> /Length 1420 /Filter /FlateDecode >> stream xXn\7 ߯вEaFtZ.. icd6_M]A#EJr΁ܸf 4ϩnuQ%eZ&}"Q`e@9-PhI;p\UrzufP۲^7X7 V0jk[Vд=Ҫ10΂4C h7+:AR|=UWX|*{cjd$K''VɶtBlu}Wְ?=oxcx}^^_='>{浉瘓OM :V,,cIT$3 g^ Fm~xlg](v:, Tc$/>׻݉w۷7w|l3 Zu|0P1ɇ<|< ?-* endstream endobj 115 0 obj << /Alternate /DeviceRGB /N 3 /Length 2596 /Filter /FlateDecode >> stream xwTSϽ7PkhRH H.*1 J"6DTpDQ2(C"QDqpId߼y͛~kg}ֺLX Xňg` lpBF|،l *?Y"1P\8=W%Oɘ4M0J"Y2Vs,[|e92<se'9`2&ctI@o|N6(.sSdl-c(2-yH_/XZ.$&\SM07#1ؙYrfYym";8980m-m(]v^DW~ emi]P`/u}q|^R,g+\Kk)/C_|Rax8t1C^7nfzDp 柇u$/ED˦L L[B@ٹЖX!@~(* {d+} G͋љς}WL$cGD2QZ4 E@@A(q`1D `'u46ptc48.`R0) @Rt CXCP%CBH@Rf[(t CQhz#0 Zl`O828.p|O×X ?:0FBx$ !i@ڐH[EE1PL ⢖V6QP>U(j MFkt,:.FW8c1L&ӎ9ƌaX: rbl1 {{{;}#tp8_\8"Ey.,X%%Gщ1-9ҀKl.oo/O$&'=JvMޞxǥ{=Vs\x ‰N柜>ucKz=s/ol|ϝ?y ^d]ps~:;/;]7|WpQoH!ɻVsnYs}ҽ~4] =>=:`;cܱ'?e~!ańD#G&}'/?^xI֓?+\wx20;5\ӯ_etWf^Qs-mw3+?~O~ endstream endobj 118 0 obj << /Length 611 /Filter /FlateDecode >> stream xڭU]o0}WD Fvm I]GMS'elBFB?#jK_q9>ju^=BH @'w3VjL`\Ey;pVrQpISUNU:dn|5oMC˽pKC}^螦yaF0nFdy0a? hmA_h B `-w`Y }\Ceuͅ5gW2(.*LxʄYOHq$T4 ]풎y2 ^}VeAiԔꛉi+=ڕ{\wCeRLTy|Vs\E*LoҴ=滔IZV:/=OF~Qg 5XiD'lQq%iuuS&o(y\#[CmDڴ",BM|u7#t|ZE~dK{B}8Qu3JLEu!ЫšC pY$"{y|uYB83{A!g__&/8/3#++i2VPPeS> &  endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 810 /Length 1993 /Filter /FlateDecode >> stream xY[oF~篘Ǹ@ɹ_En ,hHR;MDic s E8SL p*f֞9fb F0YAB"5bS"&9ôdSq&%DgJ[5S ] ,Ly\$]=Wi)\f2<Ԓi!0`ڙ@TC]c4Xg D"h{08pYǔ?XsPp@c9ZE0s _eq"9p#8 XTk DmCh©.((t a&@2*#L<#h$@WX/8h; t@7@EpVh* HH !:6H !P \J@% pjB2NPXCU@e$22 $Yו l{! J"41>qCT`]:%oY]bT`*+x%YszGyUy6pMT:A7C>+ov1a!肼JGeYA%(JM7n$@QC7 ]K(ynYF7U=8JO^'痢Zv] all((4Tq{6`y`aQ_`9:X%cn|QxF᎓f }|7,B\z LlQYwByف ^mx:C"i&]rbݏUg?EîH+)ti cDbZHkWJZ)SHq۰+4WtF0}+=mӞtO= Ds]eos Kfhnq69,f{^14'38 lcU8zjijq1J1'fTL=bB'c!|t] yJ}"msK~9h ڪgv'owuzWd i/۳.2kzc |hahZI%? z&ѐ&N(̪YQ919 @ Z3$Q]:wQ,baSIElpN~[Vm>oZu2}m~ȓ4[yu\-3N p!M|wK^/py}9Y~a }?1ö;1^Za7vzvh_Ř 䂓/p;m샋5 GyZ]Š/c.(o6A\`. .φ AnBP'@ޘ7#WLzu.Ƽ3x&/=M^ocbPK;-fw3b(yc_j~y0,AƑ`&`>:tF0vriac凾Wj;Ā['Ɣ,gQB)!>8`}ˤ]tۡq*\/+gsο၆ 4iix5묗jh'~u9aR[PpnDߦ_9-z5w4\PqC]i(ZzZ+^?^*$~1w7bw}27[|^C ~xRs?L*ٺoˊŒ_ \jYY G{]6Cn{0ͻ-6DߞŐ:y|˃o;\ Վ\z5=~;*սf$u;X HC(VRTb8oU!greij}vh ٺ_0ꀇI620Em'5΢?f C endstream endobj 154 0 obj << /Length1 1972 /Length2 22850 /Length3 0 /Length 24022 /Filter /FlateDecode >> stream xڴyeT]6n P]'@pw(P݊wⅢgޙyfVVrv>'*rU &1 G3#ȕ` bcew3r"QQI6 ISW ?b!Qd Mi0(]M5lZӿ+˛\$6V֮bp01[ ojnbg0Y䙕ʎoB#`68Z4- )u [` 7''G"%S֔2Z^5ʚo?~1Sj6wظ#"`ic˟ـ))IKih2) I: fWO׿Tr8oC*ptpxO$mb9z|[jiSu 7'-PNlDHY]3inI#f#~+gc | `7x6oCQ.tC?U~ڿ6)p{,H,ʎo@g? V6^i_&:?giO?ЋoKgٿ ۡc0qrm@@?܀o5| '[2_FR sG ` z!;m- prsX:4 "G( r1 XL,,B\o:sGuS `d ޴o7u[6ooloF7`q|#  rX\ߊ7!_?@kw5\v@O(m< X6ۛwFA@so⎞>L&v>.7+88_%N< 4GZ]r4Mo*c>֕OY͜$—ߣfQ;*JuBq_Rjo,D+Ii3kg)VvSqe'O$:{b٧~c^QVoz,o+#ױuO7Z+H &V$R?V}mJI/cvY]Ӊ=K[8ۘKMYɭ {fu/!Q^>OxgwHdqKa~.2vM sh˗MK^3a M25] ^*07RQkgK`g~vu 0f+Ft9ʢoonjc霈}{''v'!9c8IGi)}_H/S1-_( .YZgלIH ư\OJ)9\gfN<^a"X>4,quV f>BȘ%7nH*%an :.e<㹏7鑈9޸#׾i߹,:a[$DE=:5Ғ0ܜj@bF1`)sDZHpGcӢD@oi E. Gk#g +XZxL2#j.Ѹø)')^(8~&$pUUz:nnTOh=1`v%nZFB5^&R٫T9̕&h^QK1m$!~y.}s'|ؾ= fgamhob4o|HSu'Ar/Hz4Mw:x+VLC%o'D};دFZ5n.L*F %h#8nv_ޗQUHX5+ %v:: qij5[gQ 9u/d?tIAWp\.yk;;Sttt`0~ t$x餍ԘB׮1V)XW$Qs|6\k!o4$wx?VwU·ϢN[ 3P_JoGc}锿}kLdS4.ƃIۅq}v-^4y:D`+d)mk]nfh< ϭ&O8X@r]4۞/v8wiDOnH4 e=3ofs3Bc4HEOHA4QwΙ+Mt@%B@Y%"PPYm!@:q&{h,y~eKpGzy\KwǪ(ן@2/6U>b[u nwgftc\-e:i*p5`8)FreP޽KvI,ͯInVuG¹lՃ֩kZ˚{,З嵠, ,XlR]%V<@A f} C^][210yV0?э`[^w r2slͧtrKIT 3̰,3ubl 69_Q8w ^khjâ2y4s$[ uliBDͨ܎dNzoHb~%|{=HǨ\ "}$GR2٥+,n "j0h0\BTNH^vIb!vIMm~Suz 6z C.xLmpp5 R20KUarliDo.A+!-řfo1yz(1y"h 5`ЉPfUpבW[%7Yi<I:-gp|hhA]4JfTOd’ݨ1zQ*(r64#޾kŌYruñTXc7[sD[YowvKfkK;2|{wՂ pBRyG+q1![}Xغ-V|Bu*W#xؙ86ny< ְdw':oM tc#]IF|*cÜE%,{GQ(|hȅ V[8b>6kx9aμKMJ80 oc}<%,-3IܮuZVsC: s-{&~4γNO$dӠ5xzr sMeam t6g㶱qp!?H.S{lc@S/]Ņ eM ehkꏶ~ts>^kO]|.g,lU"/sq&%7uӘI2-cxEٶ ;}U0N.GvZZg2;bsz[UNԓXXֱ+X\/~ `fErǔ#'4:y?O@8nC=2PBJ褳M` tm,y30  )չ[RǪZE^SWǑbje nTuǎ,^*uݦ u}9du*%M.Le&Ɓx|U[IF:NDsV\I}<}/O43sCϛܧJhX:#?53!4N(u!#ѥ9h&[Bj#V&Ƅ+(^%0mI1.ti=x!f 8Uji7i< 7Z)4 ;L{6>UP*$gYky#-Wy9;zn2 ^HB5@h/0Fޛr.| #04 y]KoCcq1_Ymqs,;'Fô4 2>CLX "̶zqχW'oQ~Cϕmc)4nF}H%Wi-:~S_=UkP{Qyh9N3ۜ_{|ҝP _usm@2>kX'l|h )^`-R*eGOeQБbHfp>*[^MyvaA U~:CkwnCo:YvՒn7wG23Kڐ(9-}K>o.nh"@)w>)baպ]jJ0O-.є"F{"۬7ݎc_\`LsupZ8Çz 2wJ \AG}*h^~(f5)VoЌJf)/2-?q4שּׂ=3b- XwXD2?>;r/P~D2w-V-~_.͎ͪx@6vDQ9dpMͥ"xx׵RމfН\ R KƭIj;Dn+}."ج=cJ ̑iNuIk|U"$AryptM?ATөrjƃ Ki_$ ^6lpt[ruyޒI(6o;Aa(s>dsOh֝ 4 j y# ~K>ifHLxgXhYƼ |sc– .wEcZ!آx:)Hc~ UQ%]2ccn|UB(C9ƌtasiW~b1RLZ=ZOQ|ȧ\tbZ *z}P=؂%!;P&בؗne?ǵjʸQIev~ǑGI2t0eK>AճVdƯXlH'5Ͻd3QPW|*5)<hm!X+niiu[Hwspe@?C^{^9H@GJUg2.R//}k4yD4C}f~`xS-62kj8% UD(%s_y:@LRTD x\ ?VywٴK Ʒ|t7V;UCbq 7PrQR+1'Q7,e :5[$VxYGd(rT[=BUw̐=N~֜׷e'$>TR%y/qS-<=PϬz};h*fo,S,v | qW&qĩRrWt3 ̄s&BMӤfA"S >O*AZfح9qyy.1  aGi5'p;>>5ՇY"_%i}oTܶRcCZ#G*a%q烥XaCay=m'ڥv:Skw^$4ߋ|%?/$h]9YAm*3!'le}v´1vO 7x/ 7e8Jgih=[:6GHaV8"g2E Qm56}QsoyZ2eqF0&X:ΐp]v&\E}p {ndȆo\$dWyt2Ӌ~6m_E~X\紻$8fT_GK3چ)I8}.-NF,M"pxNAEj ̔K`H$ՅxZ$jWqzhp{E;\ng"B7UHjR9 I|S+FdD}T om͖Q7@=2̞nPwgE4 )Ukt j`goBOL Dh! g5JTixG%m&L𷄎9l}n_*q1#]ׯᚊ"CkLZT(`W S[b'mVtK#Bݚ؟8ed69g]s|4ޔ{'7fHw'g6(TcD< _{}޲{w]u;R%iCoP3T1X=OGb)Xw  5X$^4`4ԸAq&6|eLJki{ێHDkF,7_+@4y#6oN5B(FmXhZ7P{A%6 l#Ҫ%L/!][ wٽnѱ۾J)^OPwvvU6XtW}Kf`|9jSbTpų~xCƃ]aӑ@?b#ǩnÞSZKT[|VR-$ʵ"v+qv6;U讛(-j!O.ao}psV1vϼ0)AB^M=ҕWq7['6bxgeKlŮ!ʪhV6>zy)e%XL~m B%눛m5~`WExBO*\Q2L'wϹބ簂[U.sLBW "`V@ډ܀,΍$4{x!gľ\ ˛2,i5AK:i!~ýTmdI&eejV`9.yZs}N?eDZ08}t_96e=b -ꓒi)$<ơh(^XJtNe{]v^{F"̼CJO?Sit}Ym[S{o{W/?L3IȜn9 ģsl׏CM;vK6m )II^קU1Z}k_|E&Db/ܳ,ՌƼAlE؅Q:^DïB*VB=br+KAZ~+VP`}γ&r0Xۘqt7ׇ= Ύz223$Ou3|9!ؔN+#8oy͟䩛@#k~moZ}N-dksC Tw=quf2dQK2Ԗ T@JFLS"݇yL$B:3x_Dh>}:?T/ɜh/44"&U5$l<äKs!xڣ&xs@9L4:O7V4ZUX+3CffE xgaR{ c ˏcCHr\`|rS%itB-[7j&㔖l9{KB11 -4IDly,x`_JB+8*h3gŔ#\88(M HtB-7 6'C&dVMDS^ٜLL ZؙKTȝ ǓF-~,.y c9tzsפiUSǗyecA:=c]4 +(2GG&/~ ԯAmDY4 JvIjq@˽ϓf{D޽2?Us|(vf5U~M n0ŞQM~Rd/W~2'TC%@70x/}ZF5 }:O0*Rsۜ0 f4wt w}Ղ}u\&:‘qjKJ- -C̩g,L͞ĉr5|$G4D cYuAJJ;Uӵ} Pg-tҊY8KYtT˸9פR9:Q$MR7^1<%Khlf'W./ƪa%1 S}Denj)Y0y>}D; d1V)>60w{٬-Ueu|е^G\L$נGb)W QƩp ~!J w2: EZBXzHRI_)U#&G\LWJ\myPbw}n ł0G=BydWkzJhK,2"GmլSs;TY('UfHDq}Ւ,=S)z04Òf3Qł:򅠄K~ZO& ׀Y1UPPv=c~@hc`l@dҘ+}g{=bT=)`@,.ZA^ّb-GZ{b5@QaEh;DkVTRn+nE"f7Xu&,xs{~e%,~Tw'vUE Pҁq^fX@!&Q߂R(4'%Z76h탇ZӧVՁO<,?> w 6oI,9cb;|h gv_M搖V ͱpԗnD r{P0o|d_!eFev bTڬ!-t(P pqtOEh?/8/H\nE˖L6 I>/HaKdWEQcJzA?:ڨ]RT2YwC(9 d*_k{;UСEIw |s nR|G$[H4ʀu`aiVv.6ֆI@?;kuzsUMcTaҙN&#BG!΄ Af3з@@1HO 7 'K!$'ll 9Z)Sv` 7kxя ~5q45Cn7dN--/~kc$ˎѡvYqXJ$..O 1™8pf~$r6y g݉$<ڭ٪|lũ-- .ESx1VTuMQI_ԹMUO#bz-*KkD:zi"S5_yR|B3 ayEәl_Z7y9t6i.WWvöDO.Uʜ \;_ym*M`pq6`Je~+/#\AdжW ~O~"f2=FAF+'/:B9a!;t' -(%Pz U-aae]w*JTkqX`K0 mA߬zM&F80YRk $LfؗPaWQvw49SU,zmxOfb7,4j-_y&PJ A0t8VWO{qe7H}yL+rVѬU k# K[JW&U D:35 .dW5#/Lee57{ד$띮loLnήBO~^֦~Y܎X/S1}a}_)5\4;A@\8lQ7hN3t7 і/SG+V*bpxUoVdmvLܫot\tvcH&1"?F3~w(ڲ-0+N`6z~~ Yni:?A<- URr/H!ɲj@(N?λQ~fH{Syt,;8R]g}h;ȉe4bFk)uF+Ϋ˘R6hJU#{v S#?'mH!%[pQwf# jIz2@!nwļNLO4$Hfl nWn/V{]G\o坿R4X/@Ƞ~|q:ΑTTx?t!`y2pV~W>̆2US`?7dGA?3y7wS4=2cnfmyh=N(Z*ۭ@*& UJ-euZ\Gbޅɽݖ۰y} 6L-ًI0crr)ir*BzAiOz ֢Ѿ)YM%WѦz&a9gv Ws9!Fl6+w5ߣ{[d'O28plȭ>GG,r7@m&~Έ _kS)l9r @ܑS4*Oކ 揯rT(&]/g&p]j|=<w^)fW Eeb 3T8[b$9*gC5*<#>e[$3US獎ķZuM- ab/bq KT A}R _ˎUDk]BqFq)f=T*r}Weq2L]CE46-w rJh^ΝInAt%^S~=T%S7hjQOlBdKE+Cl^v6UyOVuƷb@B*dzQh?xi:OSFw)CeFVrd8M\J1`P2ҁe³AHxPߺijzӟw?rN.#Op?i,֤kamh@6~ a\>0.D0دy=Qq)x( GbI%ר;[%8GY١}4PF*u:Aخg<H'7ydJ<+ })oibBqՁ^daDKm( L|>SOs +@w؄jg$ !cB#T@Fy3!l ?Gv zlZR/U_7 ZaqMWT*! *ڣ<.T|K,+k\nRVw(FjsWS|&7 8B4w>,ps-zVgN2F$Iߧl;U$N #|Iю`+oc|F􁄠?* Oz#siʏӰ/21z֩m,; zf5H'aC6_HrBPc}|)$BLDKoV&RK?.f'’(( aCsmqnOzݩjr !g)8Re2u| {XbNP%ޮK@DG5t 'd6n u][wbK%x8.ֹ>^t9-A" ftͫн}ކ; Cj} ςJJI~m _afK5+Iboĸ D_ᓓh@t"+P,fy_ Mhl\:3}#G)&/TI_ 01]r"p~z+]2lD,,7-I4Y{+#AbJbLیH=67L0&yܻ5&ׅnMei̠ybk&pU& M16d5yb3Lj\>HYcGgwsZ,F*h,eׄC(]43GE)H3q87a_aÆYm+g0lw%nA{ً{CzX6lSYjZ\U <NU=s_yR*15dWQA9d-=ou?m~ įB`n7&[ LETDumQB8}q7N-Y'Yꙡ?eF3^HΒ cѼ*$F?#3HTY D5> uˁ( цg~`P,$_+}mU:%qMւܼ!\LrbY`/8@t?6֩{%rJYLW\0[]p*%[5u_k1-2$9eb7dx0{Pj?}*D*X[_YmÑЧ-]C?]]i'Ϻ#v]HsȯX^|>浀@ƕ"G(_&ޯ" ?*z&j^sI_GĚх/xs._J?kEI?N Cbpr;OyAӒ9ϡT?en4r\ PWo A7 wx ;Xا]GqZ#;n/VQŗ>t]d`F"Qnw}le;ǥK FI_Go{ΦaRM|_젃[ezI > )[P(b1Ǖ;w:5̦ЮPeu.i%.ߕrV7j<;lѮ;$%m6UˣtY!{fbİ:.{P ziY)&`BАm֯Xɝ"X1'>z]]5aߛNq'׆[ l:KAz!ֆmRW^YSmU:(.]o; 1F;z ߰VDq:{.'+/q{,Ywd0) / ɛPArNW HFëQWRl2y*6Ƽ*aɁLoU FFq4`v!n؂`d$ Y ʖ*{ #@țKeI$G]GaDMoԟ5vM SC=0C$_m3&ĜHv'w(mC[1 /Ĕ|ϗmAspSLbj)#i 9/D X8ud (߉sZYK]1[~Lloڙ2WyNO81m?A֥BR#Йoc!PkNn7j,7/ı1ۗ{GrTp 0H`#:mu)([Β7$LzMzt<ܷ[Wz{`K[vriCh=A%Ɗ؊euSsi(Q gs%We`v W\x1F(BthW(>"Eϒ֍g bM{}fH#y1A\XeNbm$f O؁E12 ,]$YEfe1VRKmH˖-T\`K;3`@fu%!Y n 1UA7ŵ-[_Grx,$m:/;7_4}!J0V5kƯ{88SbqjA:oѾOXEӔʫzLlZ6Ta}kmWRXschKƎwP ?yTbf ;r2F?G_g~:F0@yҍA"HMAyiS0t6_^քvՊ[Eʤ#N;fšB:dĨ0@:Z*h=>h󪤓^HgJsgs3g vv?i>SU犜~ :6?lѽO%-UDȋي- Wy_q]Z7@)M{\fTK3MYʉCw![x췬֥=ۓe%⹋f5bn8}6Q5v %> (|BEYDjmweAP7q2V %7ʒ%l7pf.HI|0YD^+WNgt* \V'Yhioۇ7qq^CGZրݠhptv-0$A?R0i3@8t 2пpz?rlUm\դɴ>w+u5n^fV;^][HzsMYl(> P-I3I+"?;lЧ`A4loX"t 5 rXqv.vLFR^iA8Ӱp=G*[ʑֱ2++&2iC! AMf6h񿤒;ءq H~_]7d(:/m:_<2Jb&*Xd]G23i7'@/;7P?]jg79q,Bv 2JAn(h;9 ?q+$@! *3-I羛Ju'.{G" 0p`iQC.@_1~C+BycQ"krIԂZ(DYR -nr $fGp1mW okuXGx{rHeaH1h0h`򪷘;9T 8QPN,gS0b kmeǟr5W/3r|$~@FE>K%t M29%rA6UX{gŖ]풏 VA6G'p;k= &/~vK/30v]i4SJ9X1LJ#e!$蜶 IΉ'fsl=-%`(A4-f"6Pt4zoZO D/@9˝lܻF7KYR9F≠(3?)wOߔyK9V}򖞊}%9s(d1̆ 4~b,+T KW|7Z~Ԇ;#{[cfŚ67 K= Ӥg7C7aUYFн{-N׸"/xf!L>)#"*ȴ# `gUC8ʣ6|n6ꗴȖӪb1#!N@-[$d p *xu&s-U T5ߛąR8Pn*됢XQ,.Mk7­3$"-$@z([0K''@f^@D1T4\7m(9NT4V+tU"yT\™J#m XBM?c6#ScZrI1ޔhR#CM\ÊXg6xsz͹& 6.wY=!Aq9,.y3zz:3*_C[*ɯCU==C$A1kRPwe!֧ Uf~ڈu ]vn]e[YaB=lG\IFU+ÈOz8}4Yr6G-EZ?eI_rYH~0qԅ,uG&%WMiD^ pƬT.%X?s96,Q*N*hW+P^h.)޳Ezh4oq:ģ g3NqbڱaWs`s/]Er%#ߢ۴RISz*FwFaqoN>eB} M TN4~9Xi P*K+3]h}fO8XgT-!Gy|;TH 2W<{G~jYJsOF"Jhw^HgÃAk[SͻOs_t 9;&W Ur|o _ 8(UW[> stream xڴzeT۶6ŽKpwwww)!S ݊/^ݵCяsq~##yL}\s( wa`ad+YXDAfVFffvx 1'^ r(;:Xy)R@{ӻ ` P{:Ye hoaeyw9x:YYX'oQF `loeT`(߅Vj=hilkԁ 5 U5 {`5WpSSא(K) 5?@wEw<$Eu%Xp:9[I_(ߙj+Łݝمd`?uK+g;~u*{9], gKV@{g'Iпv|wz{!\Ĵ94++쌭]..dOտbNNr(i(}ez޾c^.dl@-{?{feLADQFRBMA@ձgtpO<qy^73'ޤfb ;w'n^''u=Vfnao w2 t=L-SYKr:}́xogc7 O"x.{؛<3_l?_m=f@sx&E{3P9cKVHo+c;+[/-L_U\E-l;H9}ocgfX8Kދ6@gg'_*{ {0)KiWe$ao 2rp={f@hryw8AN6 $G/`7f08LF<~#n= 33d0d0YSt]fߐ?|Yީߩ8}]_=;owN$9L}?ߗ7d}/gh5n4 8lZVf0Q0vq~Y} (Eyx3sqXy,\\J _g@)<Ȕ/돰o~eP<'Xڲ Km8[@ B4_r}6EZʉ3mc??|d MF ŀ49:%- -D#11oW)ze-yPE,Nh(x\~cEw,aB;tu^%/U^|h9H)Ӭ!fe2Fzb?JCn"wx Y^w Oo@:0RٍAJnKZsz5ŧύoz3pNUI>!Ʈb7vqY1c:ҶdҔK{lKwp 0ӓ?ʇ8FUp%ESpt ҨDRijaoP <95'ibŨfj]۩%/ASn']$im`q7{,A_K`B< 56oA8"qh0!QC(١’zWiyG$tNA]/B+ . ~!1C¡33W?d`C[psZV x< X(HOM.u5J侇 <4vJ 9XD;M>Nle^>oUHayx$M*DO7 A;Ptbz3CiI)@ -tYt7hsM7b)D<,,V _ G-'?*6C1G$yAbf5;KQɽ;%'kC'l9"_,QGճXɐ]W^D'4b/v͗|SWvlfk2+<@ :5h֙R ||+8婁'q/!`dxTϏŞj5P]uJGxdEt!xˮniZ%c^Íï د/PiBBgz̠UE3R Ɏsu'5kn:NtNUU:@}$ k onx3} 1^}1j'FnyXg~nd}+=;XL X$mRl[T|&d<+ {\:U2*S{*խ-U6t[N+8];a^*vy@F Zy0Ǥt:k׮V_pfyݑ}$F޲y5C*z `0zd!H*<wPױZ4d(91~wtq?jfl ɹ=޴#Cw@[up#m}X+l%[[}@𹝶i0A).4l/]{JqxNW=~0A*DqClGg!Q{&-^K`o髾ztYe $RMkP\F/gc&2W\ ٭&"I֟ (7ݕzM 1hXX{9'XyގPS@ۚW$vڹ bvPA,R F 8n,G]×~#"IC(~9y:u ^:vr{>/I?dtmο]wdPz_|SC) ^; S&8 )+# g4anAI}`]lʆ2U?bR1;bKvtՓogL}+zLdž/!(2|6 T-r>,5'bMh'>4o1rq'2|k T:6(3߭v3eHf@IϨo_/0da,M8 vw;\^WYli MZ<4J0vܛ"8cnsmvj-VfUFqp?[ m.:ð$9/8&P/%Œ*WSQ /Us?Ŀ|Cں#.ۛC;w tTGPNk.YqODG~)TU15R }5*!~XŰmz_ C;u6#3/yrD.g{lCϮPr \S3O1>ApG={s8<S(SDdj~Y-SAAA=;:gĦZXeun bK 9w,IH NZG1Nl<0p@;:ڬ'b>F^T4 M/(C750AR wi| 8nHȦ}>PfṙμczD>Uy-\XLܘT$I b t'7[{b"dG^&u]y jyDk۔q]sePuFǒq SO3m/Q~ 0 eL.)s^ 4V5Z"l}cpiTrH .#?͉!$N68(FX`@`\_Yͅzb~3|ci }C1DyfnI,v8o "[)`U*ZF~ ֶ *7X(FRWBXk’%/2yWsأ\pg~At1WNu.ˌ&+B.Eɔ֕45݊`唁DsW(?O3UzFg;Q+52=YueE[fc d19124YT05-LdX^;=_*DJSn< ݌L8(5A.|e+n h, 1G9;L5R(`nr2;pԳDQ ±kblsnURpJC3(0&kwI[,x3ɺG%aЧ6(")A"  F|w̢ډ:8R ќ Qԩ'=҉=ZIWIVGhnP۳"tOn-.(K,SlAOLw~|x6[Z>oGsٱV l/OTiMod/͈* PZfc}}M̬qvǽi.Ƕ~uFxuQ-2Y$K/$_6U/ʼn)&TfXu_*Y9?HR&0"3"BCʵtpn ldwj(C7iXG铼'1ͮQm%/em1އ6#0QL`4ls@kp&sa*`2wWWBHAt/[7N{WrűïS590~k/7G:7L@%;y5pK x@"$GkIY"س;iO]*=Q$)o$j |'@pgxDhDjL~43E6?ւVՌ`w;f ]? ;Gޝa$Xoa0(E>cb.P穀 'ZA]tc?" 7h7l&\f՜y[iZҜjl ha 'XdiEyUYkVX]OG93'n;|3/LUM:<-]82fE*r_T(p#s2 cuG܌c" tžD11$BZx'/ԸL(޵Jƚ`dd6=ޥt>XJ+{} &̉=zClD?,O_3!ɸ4m(6s2*x\X?ėQrka#ybz"ߠU \X?L#P(t5OV耀Dv0v~-z}9e=r؁%ܖYNYv#;l7LgdijdzXXǦ&G8 aI |U%%$M,^3`w-~oo#woَAdaiYq[3ia\E +hA V u X| D&Eo_", @ʽ],-{zxk7aV'E~3NXݑ&:k;3D“@Nf:SysYth= 1iGJ3+~qB0b|o"gdJxb8Mf1_3q/~< zhu F6rum,cR OBE^`s0E~r+SyDkzI9 =I3Ӌp;se2hNfp rBgK,7 DlEi=##UT8&9JNaM &ǝEMD[x4\_)bLg+TxbB;Xϴ1'K o0]&EnNm4cfCMIN i\MQ[X,.!*bIߔ|EܪRssq=%rLOo6r(]kݤ[coQ?v@mn > s1CS'0lH:_Gwyei}\?.;]k ^bN̫RZ"<<ڈtstC* COH D &*=G?}~# U9H"I˸4Q4oCYYf["җ-(9ĺM= V0t<$49:}F^kYcmmĴSb*-]~v?4{+&G'6]qc [Ă Ot>lzfn0eGyEvvLgoKߊlt]O;Ev,N1q`ѡ)?>QoNcr0lUzӬuVCۤhge}H5_3r8ة9rLM3r,#j2qGC֗tK\]0m[I0ѓa'fu$LvASb),,5Jek,lk@VfFĈ_HJ9ĪS5%ĭ 9j_q 4,cqf1 OFSe翐\2V֏쎥T, ,C9&a8kYP.2xTLZ ¾OǦKNm(Wb%O3TVhi]Q Y+{2%}Z+>,dY</;p_~' m9RmFFNNVw:uWIwZ 9%7LŅNgҷ\Q;Bwܶ+=:$-H/"IgrۗTͰw61RQqcy@W[M:Ɔ,y\j;[eЉ聆]`'in?ދwNrN\QuV% #wJ[qwIsgTnAX}f&psUvڝj)]"eS*/j3R+0|u ='=O]s]PJ,ZQM.[>RM߆)@T)q7RAȚ|(:|(ZjKhNx);͸j߻-&Ԝ,w 0co㓋q/fטM]4Ț@)Lu|\ԽÑ7Bɞacs(Gn8h^ΰLCnnuҧ"xn%q3 !DeESZmF@Z K6_gI*x  57SlFk.'>d=)IoCcF4}ͻOOmFdH\ٵli V1 ԁ׭s [1)sA[Bыŭ? ft-?jmI rAǐpNcRVQ$vܟʹGۂ$8n{ X7UspQT߭!N[j=H9'VFXy3'K_1Gm[^Y+RGt5UWP[I #v׭{G g Z"e+zO[4G5;eX4=5X<8B6I W8SeЧ,Bc-F@F8sn{)gS>hvΩgţ>G 7.]ooUS@O]Q1`YN8S`#E`UqkqыT0]j?;DƁ|| XK {bub41"vN6A`rᏥ$W59-&6dt.ʤE6__1itlwc[)22dA6oN3E{A&.:u!BOhWtQ#͑WzMBMoIhU^fW#wJKw97rX?'~ *3_ΜpʆM=Q; e#$VYk68MtB@Դm*{Z8U4Gm[K&.\u9V/-q--Fŷk5q0|˕?P*8Mm}f^;JSJXZj,+=5eӇoIj讟ٗ` vI5I-)0_[#}EˏAO¾+@v'kF~\Wĩ&UiIRntT5pgGTcdG <3݁~,7 Ac^2<ˀl?SrAɶڂ.\)X(>' ٖm 5گ!x]S뗻3W#0<'R8qP Y;b kY(`Ŷs似x懣BK쨀- p{pK3j X*}契y9lh CBlzv.#,t=sDgYW"ˆ^o[RB3E)K G-hX r;ذNݵQu6MfxPv04QM:N5A5'Dyևr+V*yli8ZlYG&x"UbȒI.fn}ViY نF{6b ;XdolFmZM,HWs.|iᔢ9VtbV{55GsbIh ix#rz; ԢJAǴ׿aء04x1E`_s8_\ߔMVyb~̄Tes#a b0wz&Ww%Wq3z ŃqNUK#4̶ce.c` v82 }!B?6?tQ.R|#DG l~O'5kRKyʯkB|6#&k #YLmBl;d;.HDKӨMJ (8-u5ˮ1cPqG7a)k̏k|,̎!x0ۖf]TI| Y "L&z93(-k[x?ZCxQ^^t\DhPR+NLhaIܛo_@;ZϚq6"nu ^U$(fnpE 8ACؗ-Y"*E$v^_XgH 0lHgoۣ\^zR'h0PD6zmSn]gPA )?D$4x-N>X]\NSR w1'JcL$ƀE^-oT=j]ϙAs#+&[Ê)q2R+0hObi]ޜB) MuH<#x/) 26+}eePlsz 9O dWI_7X{oEAkg@ jVGMN,gx|9? @A٢XFb=8yfCsvƵ'uZÚ =o׳mܡ>|=ϧ n]l[ BCW#g.Z43ar=0>YЧfk@Cj<e$yХmyS7Xṷv͹+Wg(W=/w~~l9F" X(ϜN ̌~eCL_u.<9z/db<-ݬ |O>PWNSR`*vZA܇fQ*ۚqcsQ1gxyoֹ ~؝xm*"[5.J)38k9%8F94cWl.AEn\.3gv8+,d"% !-&]wl͛P]v|~BS|◩o?c;Gܙ7~%70zU"Qz(c@(I\|>, tMĹS<JNYx2m/FX ;N2+G')'"oBc kg90Ӈnñh''$I2wôUu@&')i9V eM2,\Kh>i0\Õ/ hg9܍N> ;Z7̳ѯPln9pѯ)14$ׄx!e>3mb Ϣ锱3Ã6j2D0ΨoYQpS?Ü(TQt4ȍ$lFh~m‘|EL>$~ms:>xw($_׶/0c6^,E~&8ik 8l|/F9V 7eewH 1j*=ORo|k&} OŲ~<$_+cErt^ B5|oBf(Z$:$FceEjGLu(po7vu6c&>B;b^=A)FC?Xq<]"r)J/E10@z;|i/!^]hH0pEa?S*Pz߆H.RM#I-83' o!~3 Jc)_ck(Y*%w颴- HV Oؚ4_07tؙUC&9.w~$^wfc6||E{ 9B5zP1@XVw>e*?Ҫ0]\ {J{yȳBャp.R) iT5Fkl5fF'uM57h DZw^۔W?^QP\m;6t`Ij xF\?CR* "ы3d,G94~D h?s]%x,M{ i/߉o2^..@ #C-[n]Y?ubjq]*>GJw9:1 j"ڬ5-^/^Θ:Y+͝%bc 6EvzLq1ַ_ %^[9P{5"R(!PA%H}_+ݶݸ.;6ӈs`v˨<:"MӅfym^b`,~B\WyWzPVtk5N oj>?.=NVi%3l5g:~ s'IѪkϋN$ӧ,C?5CF0^zztp>Qq w ŵ3 fY}^YItEY[ޔu[VK_ICOmo"J(ZMzH<ի(Д*BV~u=;g#-u¤r P"]1'3ЦhC5buaw/85ÝbxYKx^?85~ICҠ(P^]b:PLa>|LliA2{s1 6aBO}SGA+RgX+I:R&˞'P jzmMMOo׃c̶ORNY?8 !zu/hs7/͍m`8RsUʸRP*HjZӳkKws)P)~ wgs ě RPls!4+U|IJLzs1G$C@+, e29P 09A9f3)AƞnQ82@)t%4e/Arj~_4;][dK>L)!*Cv~ g,ki6b#P:K6 <%^#E a\铵ao* +1&8m׍͓^lxLgJl3q({W%:'Sf6LhyW8],w)A|GqBr|G"j G5'}qh6q-l=& փh~'6Sj 84tkl+bԯ t)Y'TgbR *zwrDT{33$,@傧O, pɞKУۿ(E]f&Yc6NYl[D[xlq8@Ps"}bHjRx?–SHgo}Y'miq%4 U[O){YaU7T[YPA[Lkbl%ͤV3'b.0L*A:qyC ̯tLEa1ػaDI_qjn.#v;<9:OuR0'söghs*^k*(gO`15;hbzb֒PTVb O"ɉs" nOyZ_N\}DPBY8$أؑECܵ5V9qUq;~b;nE,F"9x'Ez7iƌߐW(wWA jW;{|U95QTb"{C+/ִT!$)nu*7G[fKb,q_+5*ZxqFn*Fvlng2-l[lB'gn>[Ut75_&r"ۊ`*IzUsO*}nh_'@9-%ΐL+܊uRt!+9(QTY$`9k?)8-?Y :WGx:P*d/s5GtTpJ@5 $ lQM#D1a;$~ud)}<;gp ξ$v$>$BЫ~Ś,h_Ը)yU!Cq급dN6BhtSnr8ؤt: ?%;r2"\}Ne{81vW PPpޘoڼo0_346kZCI &ȁ.^R,FE)a8yl>TSfgpmr,[렇ֶp#J4'N@59C.fWowM`VaYPZfVǻ fLNΦES6OIgJ׈7U#žSTB~6%TEGwݮVpY-wKq-dl;>) (hTMRkfxnr$á,T~~nv4 qiC\YbY n0TX+;.T|K,+j$m"/ #TZ.;a.ݦ ʢ195)Cb\Ob89>ƫ&MwA{ sIIZhu҇J+3؉)UCAxl=_7I7f5"G)hBS/WK4}~C> FiQ) 3+vMHF-s W `7X5mV*[!+H;mB=ݼ0)kLVba{Ҧ(9#h֌(` ?T 6OuwE}lIۺ2i2 =qsƗ4c_ \`DQɝ2.BqE?G}u?HNDDfQ&04I\$,M߉? T~d=cg1eUKm1ܱ-.a~\D,.;n3@QJ_E̛ A\NmNZ((i R9*XMXX;rː/Y#ʪcĞf^"A3 $U]Tfi\i=6bb(Kj^7`C8!f"ܖ<=*jNbM )z vbo$1T$V`D(cv="&rYϰ;dgIEOlE~CB8J*fWԃ)Nϐ}JGUX-(nafIHZt5fxiŮ )ꕕlQ9qc} 8an.XT2~\z] `_9Q>Wu/9/K^x[N~4 ٧ krL8 /+/9Kϙ!8(տ9^ }pi{sU"ģ~MWQ>B{ur(n p.+z`bԌ!!EvpX?WUUsViؾ+iKYb*.cӗjxٖmǫY?x. +Iј#ms[-)SUq/.\Z B,nPWg}B;1'c~Cu^2Q油uk1x0uAH(p\Iܘ__ؑ{,ڪlDPp^쾀0z67cTHZ GZmfʟ !ٚ_籔S?11z fCHǨD癁(klܲ쳆{[©_ZS m^>P/a #T3@ IU")tYtY?2 t8#-w{z\)EWv\Hva/2}X$Rb?x?(x>jEXn,ŔM}l/'zģB_+Ze%m .?AfM>N@FAkn:g*Q{LT )X=}q񾏤T*jDC.MO=z{aHWi\ S+Dぎs,hZLM8FK|5o4f|y)7dtd(ת?@~ V!U "EH\nqTҮiHQ?1(X!hNʂV~'gPTFG&-,R]vؽ(+T @$=#Mtt{eb1͔LY),nRoYQp&,uElKX=a;*p|<r /ზ¸W yyJ͌ѱ1 uB7ɐQxgC:sif3L׵FRjXF%Ih'&dzD fsJBH2,U$<~dTe.$6qG%*UD8Ue3xw5: ""&FR8pXR tf0Xl`Gumf|Crt 5MQoM HgO Ҿx>VH01ŻWel9 Iӯ~nhuW|8Ok @hݲ[фy|[su8Q](m}PgTuGH!mDwiYo{ZTq|u"0N!";ˠ~QE(r,=OGJGMl륷ZU HKT;A)vw:2 b7NO""[҇JJl%8O.V5M:ۓݫxKB0e ҮBQy~֔׽ÁqG|,BɱTt>n3ISEN~5w`Op%Ǻ6Ň <OFL[V`"F;u84GmJ@VZ=q8zn[zz$:G:s FBJ6kfƬ7;QN( & ,1k yWz$Ȗ%Y:l0oG<. @l9H.Dw;Y<@fL' [ g6SK Y3d{V}N E@L]uHEsof4Tw)D. #?B@#ysGfNHi_$?Q^R34qq |{gǬLͫWK?_F7];瓱J$6[c=> 3nޝ1ˆ(T _AqMA_D o Fo)H'6 BsQW\~qj3GϣnO۴-g,x 5!8RdXoLjyf--/}C+.p˹oI';,PU$I0Lm3#@*|{'bnLeb˴2V3T&wrN* d;ϓe <22 ^%q㖖Ò552yr{l3MQfفNJ< *7 Y(7G Mtt0 F9J5CSB|KG ů%܅Bf?Ød'˧r𢌌3%*KŽAq4R6ܱ Qer2T!B:+- Ăc3Sv4u{ iT5Ech6&hMKCSVc+I7]3X7(nU>YbI#mM E.i C<5f&@ 7oPrr2aM+uSsEDpEI,(N |5]|p p {f}$[ˈnVkQu@N3|ihbnb+ ]j#PʻibQ^S.a-ֽw)}Qݕ+$K>"(Hl4#3U~7d}'M ]4@lzCc\=ǰ3{h\z"2ED f4AOeZv)4u44.w Io/(;p# "{BljcP s].W j p-sBI:Ob+IaY%6/yX`nE*i8 ڤe6g+K_ =7+^ܼt,KEhH:) _R:.x%ꅊ%jX 3L;Dl(c+/t}韺G>U(Ne$x[ ~7s jh1u}#Nb{eJ7QϹǥU k+߭3JǙ pR,5*dF&]E{N|U4l!:*Q\'ɲD)Y4OfQXw]Ƥ +"e@J YK#wLe (-2h5Xv@S4YӣlGR5DH:11@l޳.VˉzYlYZ/y?*YhBw~j3?(>SG1¶B|#n$ݗi9:[qӒj0wɻVb+}'FzJӻs+ǑbZ+$&PF[ &U%˭VWl'eӕADaw,pU=XzV?Ɛij*~1&O|Kf{}vMc)5ux@-:>a(Ծ:uHɝJJ6IJ ;|!4#c$a%Xϊeo6 0., i0o"eyӍef+84lx7 pj h`!9عo%wz$o w[<#\|b(ƃWkVuQ#H&33ƦCJ"#f5N YuWC!GY~ߑzYV i~q hEeVf#WK=>7HЂ82)vX1Vv, ֈ/3#KّXb$kn/ry?.4E$#\p2f}zu?3KE:q @qQeJC*hD֍"˄{q+ nLRo^ִP[:!oFEեNV2<ڿ84ȅ Rf1ğ]`5q3^03zd1uvh/pd0T-UK᭸MOҏFc]*Yx4\%pS6H"0l=(d Jp~ܒIVF0=l,{JQN貭NVF#Ǎ .7hއ8W0oq >+4<,XV0R5LOFdьnv2NExjer<]7~Q q̪݉Sn$u]?pbK*hU;sWޕYIFQnƯ✎k1bJ ;/Zy?LRJsxc"1 M 2tr*_tCfb$*A:o5% '/ ?]qf泽6ֳlzK52IM}N!Oҽ 1N )VsyU+#U@'5TL۶L,>QA ga8l 7,ɿ%؞vo2PFcmXz#NVW!ˢG! n=Ogkbh<1~}|'8$olyۈ-} [t_aa wc!CxU 6,a1[,YPt%Tw_<52$ĥ5 %VcxA_&bxL{v}U2g`FnNqgzԸP?|{x29RQ{E=$qÒ4BF#*EbW>};±#wXUg'a˯ۡ\Z|aKQVu}7ˆWcicst Nn-0Hw0g/\7c)A !7O0H7>^ə endstream endobj 158 0 obj << /Length1 2726 /Length2 32406 /Length3 0 /Length 33880 /Filter /FlateDecode >> stream xڴuXߺ>LwIJ ݝ J7ҍtKtH7J7Rlw}/.~^zg EC,nlqv3 5Nl k F :;I /jB"Qh 'i 0(@-;Psv3!n"fkm]w,-d P`Qf8{Azg'9`phښY Um5MHaMg"-WђtښZ?j *Z>ZZj쬿`xm/nf? VnΎ4ۀ.^^^,`g7kiغn ?x8YBۀUl-@NI2r:B$A]_w?URSS8m '$ {AAt"Hz._ΐ9{ǀNi˶pvru"`e:cSWbV 3D'7ĥ|l<v~.dH,%!Q~'e ᰲu H^!&?6k@63/!\]V@wP  <@~;,m-Q;Y9e0_ ?GrN-| +Vg0d$9iKA4@W.7[zg7GlelAj` I/<q'kd[1i>Rم<l?\<僌4Db Q7_;6I;Y8[:Y8y@77 d8~y3,V'g0$X9PnoӿUJA|V?*o`UA\V?EpQ \ ?E߈O H??O !>qu!ho7*;b7[8;@e~[4bgl SB E@R \@+?%8CϿjwp$/!i2DO__/Q/Qoz ;d*sCR G/?d@ Y7 r8H?VPr~Y] %\%҉l?\!\Aܿc@ )n/d@w C! qeuJ B$ By5l _rJ g^5n ][KȋB`7[oC6ȭC+h@B+[Bۏ yrAuހȵkn4S y,P-zW(]4UOr\/9IZ*$ZE[$'`TGޖZ=ym. T$ƐaR^ d8P+5՞N;칏xyuNiTѾZU2ނEE8 ~M/5+|?׃ ["+~7KGu}Y0ʽ -5I,.+s@ 6r:ؽ#P~J.7?畿J{Y;D[QH Ƴn<6 P̐SɜzX`Ɗ=*H BK4n߲ZcR~MjǟksQu&V (3iNvVMzyyU>_&V[S|j/ U0 kkK5폣s2+)ϩ:9mWTfC$kd;3#G NBp<,Wksdxq@#_ Ybɯq } 4*J1:Kѿfs?K$O*C,Ui_X%Sn/'^x'&GS8Nw&'Bk(&hYڨTBP/ Kj(dVyhءۉ$Czwa8G633äQ]9ǮҫH5n%EA lY{$|z3&!Q#8)uO ;6^6 ՄY~|pH(#*Whx&2 "?ggp9 B6K5J{;3,GR 1yFT'kboŘ:Q- >!pxHZJ ?p21Ǽ\MMl&o/P痜B+`"*ߘjWp^pפذ9huf&e\D>15w_wv\8 'D]:'2 4x4zck>_?ę6,d39ϼ;ѣc t6כ W v]\ilEj.ϒ_f\Q"a‡V\I}g%c_Wᅣxv,)BYF iju;t4LwWIs^뗇{䯎lʔLG+9smed++Z6JGŔzE='k^\W͉d:71IomoK}7R|YU/WB3yBQ} 0 ~y_*mNJ+ILWS.OoQVz%K CƣjI9\i!gDf~2?i]{\Ё-[<96KǞb.H.iNq'IGA~dO""\p鿾 _qqnEsg_R5-Ƌ3#н@V`I[%;7Q?+~Q-%&kD;V KEkR/:^.ԕQ8LmNޗ-j..,Tl 韂&bZVH p҃'{S 3 }~ϹRJMlTlxׅZ)}\}AqW#w(T'6Kqh誶jΩS^>Rgj敝eۥ{m6p%}tG#ntoM0 3Zđj0vTgP 8\6*{"NF yōt7̗ҨMWt;WJcsO_PlQ< @9hm\Ze@m`S\TBn%@bp}^g,IŊֆ_̷XOEҡWc' D@aoxL#@;)Ե0hFM8JL31ոKi"al9f@^nb~twó"X_B8i[-ԮqfS )^׎b Zc]r?3ׇƪ76 T/C9.+Iq6IB{mMU_>7~Ѫގ cׯj=Rr7W%Li%8LEU~2ڝR1aú;²RKgOR71'NmhV6 [%fF찣F竛A~@CXt0E C֧}h6*gW[K&Q!rM;BesEhӡ-)]Nѧ&9moFu97P[QRfB'(XE_qln ?/YB uH5TҊEwt:/,1 Q u0ؕM_($^7qv?FlurTꛮBzzwd_?[JMaF$8N HZ4}JZVnX9SH%/J ՑŒ@/n%9LҨ{|n |}QJJ/knKwHe5~ԐeC(ڊ>n:o122RitvG;$Ɣ0'e*HZvV[WU+hOi1XVMFy\=/[3$oD@ٸ9kϮdcZ5.|MMTDآ*IxE] 8uq^?9 ahO *n^Ͼ.cl5OZ71&ˆ ߀bhVOzΚVhMηӡݪWwc"fyC#0MJ OUbDOz|TM.>e ,8`{Ӗi|`3ݘSeM0pWI7+9r V o<;8FRK]mdy gaːhJ\ts:IŪ pX\RiRuOAE,'qp2I)'C @% j<;Q3SĘ^U2gbpvRMhZJGщ#mCJ7ogNb_95`.e)4g rD =w1_y;~I~Qoڌ1QR$y*:i;vwU3Rn_c01IdA|a?k;S-St?~6\x )PGFGw-fh9rTE+n%k lg)X=& yQ^zBKg9e֚0vLM$7,xz-cClzfM^!7WүmC)FIU}a+-^Am0·RlI*uU'4Sy:ע3a=b7iǨ$l'?qdaHJfOnOj׶Έg{-%>>i_U[24-#DjC(/Hu C0)% %65q7SGT< ֥ѭ*(?h$9n<1(TKNFө)?X=o'\v6Ǹb$lQw.lwgg] vO@BiryF]Ҩ8IhSlF}\jÙF$w<NpJQo?Ύ@ӊ>VtR"zY]}"2Qw`"Qp۫/J/MÈ_Ro|9%0ȇprr92k+Cdc]ߎ[K|,ܘj$6_Ll:xjQ~U@o|cAit gҭٽFF8""nV 5{ n&\iPZ뵩%Rl'V30BHgFƮ|S]`31imiev$==q]NzF_ox㤣3mVfҢOPlěS]Eo@1$GoUN,/*OJ(;F5r&؈C$} xmoA_eM7ZEO ގi˕gſ yU]AT+^I[]"1Aq&q3RJC8Vk| 5Jk+nf$;vT,o(pRЩT@z* >+ M-"ݶ̈5#,C{nGF g%wOQ[/cޗs5fd>e<^w ;{2׉4ֲ8r.rXz4*+t8EnQwtf|qX]d"RWZ_~h?D6H[0] c(.0UZ5mR)@=?sb:+s*:鍼Pljx1퀏ǖw&A?_ȭ\z }ߌjy;AFfc+B%C 9ŗ"Սn+r|<%9(/_A?{ 6gLld讲xH磮&7鱽FלmI8im]%&?n,vC#?, m"F#o1L?sD6e,.xei[h.8/CK{qE(GX*Ow(Jfٰ/!78!I` 1׋?,x0'qo r}ßzLWot2w)q^ˉA+$ R+JFqfs(3%f_gJe&Ew9M$y]af5O$3B+66_||gg2WO(mӤ̧Wl7gn8,u"iHLQuy穀gqեX{F|@:jcco6_+fOyz݈8emWВS{trK+z~hsTcN6DhdÄ~CBS67z&{鰮{zL<~w9dyAݲ,hfMNDqm(=+yLJ,F)lM}A+auj}Z: 3n> gd-GpjK.pe4{!ye~|ެ@%%cFBc!uɯp˔ƴ/B \4ۆ_e1Qn uN5zFKk3?>k7#kFb uA]va^ Ͷ]dFƓR3Lwf24 u4 c-B+ u~.No<թ8BXHw+{k‡EP ~|d ֆ?Ӟ ɰwlJKoK"pAˎqϱ1hWS#WվwW-L\*I^ǨCr=ny 3SC:W'D9_lNiJ,D>贠_1MтTg|DhGb􃚲AwAv-%ZFb3fzQ*]hZOmDB7@=V H [y mkCk|\%x@߶~s¹xڨGfހƈ|i12B *;r< wIg-5*[V _P k%Cؾ7U˞8nb]=%>3[PY{ʘuqĉo>2//bǽp <\]P#NPTTH[_#Ȼf7b YfFK1M)8ʨ·`/M%^犚,MȜd\kz`zoSJkXsjyd 4~D .6a5n_RL=vP(e97[pc*=XXryDZϯ5XbFȮӮ>jaBs6s?w'?!hc@ȼt^Ew4<0 7ɚ$H~ڃz>$Et&ל@ J> c[92xEٔP57Wy~XeP'{Y '; 8-[md{`ͼ.2X>{M,@$vZ3c~VVK຤ۯtB{J83RѷdóLήT!6srM}hfPt{gGY(#lv̓M#}}.Đp ݭ%#\~Sr%1RIV׀BEQB_a(S,>)}TG+xWwA\cOSfKi6z'vsxX$X˞Zdrr1&h~FUx~ĬO`S>s'k?;2/@v)tucRMw^&bGFKvse3f IC=pOL I̞c~'%faVi RLaI%%S:Ay:1Dқܱq Sxɼ.Go4tg,Rݳj[3VХYۢDZW-c(]URJj !gÙ>-ǷuHkMdqa-0b(9 VAw׺BLtg9~2[wr;Sm z<RG'=OaD,s}&tlLڨ;x u$6k+~tmꭁ Z1]n_ <Jw{ | ryGcPPHLb.R }z=]ݬ=<¿z<iރpX~E@EWRV޽وRCd*`2^}d]$emzɾoJm뿡E;DeGOu+[ﵡ`<,RܯU[FgS~*!&o~0h?_ Ǧ%IXā_b`VEuGL1u>#ՠr&.ʽq w4oS]qkU00dZ>=;|D<ʱ m 7D$xxwY;(hY<ƙs0|W^Jou{}@5B۳qTfϯF,|/`ώ౰syNgxѓ/G. >JZJxzFwlZTZӵE"0Q'Q?UmDs nesᄵX345} Tv }3NMO;7+Y>I}K6|!-`L9̣.(n-uS"h_x|Gʆ*Go O"L;]tl,;{mڹʽi u6& }yTcLuF3Ie*hR.\f_x3m@yPrXhsa{FOXN@g 9 Ji%KxGLݶ˽@ƝY*%ӌI1,4E7&_FCHDa.Zh}ZWMnjZ]QԘ/]ZK9'kkLЋ ^pT0>r+[28K2(ml9I: +oEh9EqWJlhF0|KRmW?JM1UW oƧpPrvy?aAk"# `S0ܐpNRi&O>48PknɆiS#!̪jm~bԬG GHUVGS AZJ "}e>cP\{ӣ>:INi)[('8y/& GIxNwpBrGeǵe'Kc$`y p%P Ryf)3c3v+ ΅on u>v J5+[e 5zb?,9\s [s7د*HfcbĄ)*cU<%=$.X Q3H<4~as"5>79]aW_ Q kuwk" ׅD+ Mjca֠vC=DbSDb[Lw=SB j'}νF&t_M|bS&spo0K X0ג >/*O7p {=W^;iWTMJQR!Wn1_cTֿx02hA&lDXS}WcM ?CGBZ?HR_NGjb uQĞq*2+jC|Kª}0c rnZ23,XT9Kt)R Dۥ^ 8˛FÙát(VfˁCQkͳ˸YyE?M_HcndRh#Eu?>)1ը@-9BdNYEڨ# ޑ#R[tdi0\hO?iB\o:}rVyo(4?IHs^I 4`[!H.U=:US:ڧ |jٛ6ؕd/- shΰN>h87wģ}4mCoi~ee=4VHEdˑ:?飡M>Třr'݃D#O)ƙ1;$؄e͢JuY]ݖUTQL)%th1EY9 {aTiYDգf`Aei 8#K6^'Cu;`y%HnD eZO>m-dو-TsG{К}YIC`wMˈݯ\$$ %l=B!9shpybgmm"[wxsz51"ʙ^aT96F6uЌiH)dJ԰^(Zn9Ǩ 's+yڎ9BEW8g:GBp%e;0ϫ<ߊrF#&䞞DHpY T-Iƛ}iW 4OorMdYRl6sX@`ضm۶m۶m۶m۶mo{C*ɡ38*[dbe] j;LeմT&3i:`Oq-7Q|1Ax$we6Fd#Lj O\Si)? hG6|j-n"XSO1DPjFt4qR*'{Y)_::ypQ/e^Fǵ[ӂ9[U$]DҦ18°u &҉ipF?N,GQc`3}@<_loҥ6obBC[S>z/Vi޷wʲS0fmWV Щ}4 S=|Z XT%L R! G"eM)@n0u2G泛ÿuWGx=˵'Ӎ6ɭMٕHaV`/~W 0Gvǃقqc@N[ciai,1Sr1H孃&tl1&0sn/}bK}hHo|{-+!iy#4z}ћ})?$1:Uē\g;a2E=dzU]i^]D.>P2nmaaמWξF y*%V;f (r~fQOFPfgYmizl P]^N~B3wL mҖU UGH<#5u%DfRs(H#8J]f It&]19-FQBp nɬÁ~ IXA$pZ   V!FAaqgvhd'=}Hh|UZ1)!7+CBk޾yYWkQ3'8+AE):U%- K'+v"^VBng0L*!0:K29&紮czd@)n5/ vĬanO{dkJ8:l1Z<\ 20/@fmLX5@6g%fN\Uǡq|yq!Nvj2ȊJ<[UyUt nfu*`Ԇq쀑\ўk7'Wuٌ5zGK:LI$Y lڲֻZe|B(ל>6Ձ1dTP)><6I~iśMi>+h$rԋ9]5"+5h䝾G_ c ݪ uI !;V`g؜ t]wZLo}0R f9C(ش|ȎNm%qew" 9P썜8YbwjT^VtR &n0ostxĝTc[#kGqYP#*] 1 Iޅ.[1;tfgcd.@)cKxci=m?#4ؠ7|ت,~'qPHJ(*YVG2shGꞤv #h;8]˒3,G_\g̰g4rqpN5`rf[g#K3Lù䋹@$KX9F"q JBmNbLF4?AAC,TJ ?`ho, r1gL zte$lCu>9q-5 oϚ h꟰̯ղqB|Ov䫄i(zB92ɞzswAԫkNpk?.[f_`M vEnX׾T͆w "cw ȋ9G0}.5*WR4A{M4d_JD7c s+J^"uyLwy#m31ۨ['ۀȜJ?grhP\j< hL5W/9bowy*׮I$F$pjF}h$Ǜ;R yFցI&Dz ec G\U>7sbae5>̉Va4r-ZK]xg3ĕllv.s(`Jz[o^;My';I'7 1)ayK'2XsrOE٭giɐab"MAGn#4$VL3(~.﬑@sPu#^X"H^ԋV } ߕEܮ'3ʐl.h\Ø>U@̀1H;"1rjY@V^b"|Z*.PreJ8hL]s~*\Э3pf\W}q8]*%P\Xϐ«rB }|3C>(H-vrݰyBs$rB(1ɐM*X`?&#l^@]xSJJ6vwD^ez%jlX>!̏emI.F#J);Q3C<ՔC_RCu`yېZ/"~O;1.fmq c%! ޘҐm9oK5-?BPKDz.-6'a\NQNyJoxi[͕qAy,Xbzy :-.+)AL f[M 9^1"ZE-mf˴13l+'.m0/ f/'c[M'O gT®qgaMޅ76ؘܷ`tÙwzB(690By;hm-YOJrw5biҬMaDp33=}~z>N<0JfwVح^-̺?B7qF12êarN| i]t)W'0"#XP=}%xaQ\1bjxRLA[OXښeDgQLԚn[ge$ q|nWʅf;*g=U%23Ev*޴Gzf1&n /,K3LQIp?W[b{JE0spǹnv*6kUCrxX,AZ :6.i p fw%g@b)RsS:iOChF")RaI-{6<0X%N{X='pF:8MLbS6D("͸N{4Ђd DC#3m7{93;Oۢyo_-H]9N:N'a%5\6,$D&vvKÌ:Q6!Gjͅ}@ F[ ="%y_=~j7Iw7E)_J-^>H䞊m4fE SLFa\vTCj00oFP_TWBld_SSؚnRMX P#3iPObI,^Ԁl\&D쪧CYANg*HOGTj6(jj_v*Ch_;/ҝ:c1g4|-}F  ET:m[*|`u?nQ)2,͹QS4*j+Xpv4xBXˆiSQΨ#1*MTXI":<-'yrDZC#KRkIZ<:1)9.!5۵xGTl^ "@ja^4VˎMLѤCY`7ρ$`q}D3 @ zi~ԁ-FW38b67|/"xR_? Z99 ,?P@K3 $nuomZ*E?`CR#-INg|8-VAxt,`kܾ(6?Tbu5N%U*r:^@e/ggWHZ: BbJ_7bKNmndib*-5鲴EџfdVG&L2aZW@>rczoXR60}J'ɜ.Ƈs[@ћY7hy],'OtțcE%b*&馔-iVyd]R[ gri~N8&  jSQ#Z j"kTJ+j ɳdW]?PI+?xƒhܵ'_&|$ZƱVS'+O9ΰehu[>[M T;E6`~Aw6>7|4n j7frLyBU+y[5$1C?ikޛkU„LD=;'mJ ;d/ot#28/`/A[Ã9jJyxg3M>ȉKFo~zAx^7l J_|6#Nˇ1 ?=aVo)Gz^tO!AvR 'ZZy\;'B3HYPQH}&V¶ɥDWGP%@T)ljA)/R'Cdiǎ +V88YBŇx5q,Y,"T&Q$p(\_XZL~ <;&w/LO"<E0vukNkB:aq89}, JE+$, bSbyU<,'Sz^Myr._E8^+sfT: V{@c5Mz7쒘!Hu=9]MJAh Rj&}Y4m)ҔsEb:H?:kiЉX[m _SP&}\,?Ym^y:RpU1$~lU&Wbu YxW~7Yohm[V-~튣A8Un{|ͧdU\fƇ<,RIs+8@"rtr.\rMWbAZ>dȟީ4@]y#:%~yz>Wh?\Wp4qkX&Ji=7 kxqʹh>;T^gVinUВ*brr-w&#*Ú>ћ_W3Y$i )Ս֎ڏߌ^Xl600&]v"Vz3X4FG{Ƣ*CDuCĚ #b|VM7X8dconmÒVKd0-*$|G}:a*.9;W@}M@ِ [<Ζ;4oiq/AV;JUdZS,rA%ztS׊ #łͿ&Vo5%  kNTe !I^5ߡy!5ǯm^I Hr(f}|Q=ehU@E鹙L Iu -:3+2ARL-Rn^޳KJrLTH?B,%zfvZARS\6db7p <'Jh5+>1Y1) ]o]A0.l\9drly%W|fʃk:/)q*)Np:A 3|<:|u5y^ؔw yYL6w6W7߼ިXl'h!,^(q[p7C{y'S?f^;4~nekJQ8=J8w/7l ĕq8u1C֑U2R#:7΀~|CEߕd t؏҈Rɵ+r 7ooo3NMFh̝pC\Ba{aF©\S>(!ghrW¼uF2p2NN @/?P(?NtRwJaA-%qj0:7P._;R6.IyEeo_f_O_>]CΏ29TВ$kE*cE".Ƅ^;/6ޞ5JftUsw(O'Kyfn.Q3.ϑsJ q)xm*19[)]i6_AzcFF͘k 7W:`@!8~X1߇mwNL]wx@ؖ Q `)wo\A,|8In74QRQXg5%:7FB u B P /\U|;vҩj?m7QW%^^>Tx܁VQo"Ն# ; USXo5p9Oi5c{w$C{1mN[(?y8~ dEfS1Pt=%Y ŒaZ7KOOG0pGq`FZxkKX b%>geYDl_qk3* ZBvB^,j;'L&ǫ~[tpNbΏk4)XP]p]s_8Ci~ v =΁0^ ]})k%@%9J>E)ݮ#I81re$N&_J6I jQguIeѓ'@qi`q'5j/9mW.A)i-g4yr5u`+4yn<}I.ꐣ"ж6µZ  .X/KEǙtP Z#SJ+^Lg\$AGpԞz?8 7ĵE{c/<|dgx_Bϫ~'I[- [JBp؞+tSAhIǽeK\P`6. r,^^}qI[MD}O!䯶99{%-}:zRb4q#bya'=bT_cP=iJz" dÂׇdAk0>O<ۯ8X{Ot vFV͝G0Fl\xKifJZSW:r9,nH$:w2F3F1;p ZxLaA,=q/-C}xVY[Ṫwciwŕ4)Lo!%! 9A>}3½~_e[l6c޼犹`:,ji–\ =YMi`M~^T%3Cz!IK7RjB\qut㢯EM[Io<$cRdaFDgHdڐsSO\oӨ ѹ_3{|05ڎ64=PCv<ۆF}>O1F<  *b]̰]N_ uS0g5pBA<\s\GrpAVz"tPĤ&!Fzc9#\!Zww%T!Vxmed sa@;~Qv4WZDA5`{'s"\ a",&'#kn8Ƀ7% \ N*qPMc:.?jlo_sD"t pS14rڲZ$?@M]^[r ;hSHvŃ6R2z}2OM'͖ۆ# n{X꟝#3?b_7񹳹Mba8p>!9^ߥ/Q*o1'?!3_}9#VPv׬F+uv|uX8?1{S5ȨBcg/읂6í+ Rq_82#tj5#|I^O-K(R mz^1vPAK+Uw#¿j9;t. mTI z\o<*;k :ËZꚑ $3vrB[Y >۱Np*QOh=:/h/q#P>9|1](LIykxRݱ^~,þOzi`ևr~B5!R,L*r):@ I 'n9&s5FssYa!e ¨%%UOD:kaS~YhT]%(#I.+JNJ0\82/?8 Rըq4I"ǭ;|Lb1)ZR"q Q ~P(z o104j _^8~Re&EQP-H٘=³u 9:##5@Ks2v}VAY@,C߉|8dvՙ( [O;ٽE&)/JO]'w'bx;^ɹRi7?F/,R }(9)D~:Нha|D!i"ݬHh}"vt?; Ϫ_?e ^5:PXܐ)S|?MhWZ7Osfyd3Ho:|Z$>ơ%:.p?z֕q$͟h@au:&/ dajö=F+8#1U{*a8iǣSLL~4D!7~-*wN+a(Kތ֐Nf%tl}m%07nţG~mZr>3>n8L5ET5a@6: |e@C+H!b`)(ӟ\b͓/L:gQeP8Ta"1UӋA Mtx tݛ Xp+'УEL iʔl?w<mn2N#rl"0ic> 8|\`Ys" \H-]C%hSWZ#wj56OVoπp|H|\Å «#f؟7/ 5QGW|[ p+d055@2Pw1n=Pg,f$ؘyM d?q&ۤ_B"Z Gmsfy`Bѭ eBs+gPAH-MT,eW,L„ Z}TtV6 VY CK"Y2? ~a-\tЫb=AӠŢdSM"U)."~kb(CU}Z-[whMmF^}U{bSZ3R[E&O,U*GqUsV_Yc(TխO`t"-GοyqȵF_8Z ɀ ?҂HT2mx@BLiդ]#v a7@/?npkwHȰmsa|kX@?*ϰKXwTGi<3tD6%li ci|!&1^ΌGC&r/Ωٷ5 =VLx{[?O|$X,1g-nHO$ sZ{LW?1kĭ`DH6/LӑHb?&-(1{XpFr!,<)g[h&P K)^{ybw8eo9_Ï;ŧ:<û*j>$WF{O˔uaS/PP?]ӑ+aٜHx.v)Yv{;m!05<6^"jܮL…Krעm_9҆ԥdR25vkJGlk> `p3Ƕ'"`Wiu/@ޗH cIQunɼ7 P!U Ki,p݊EPop1D9M6*XS=VT-P3hN>#'f6H讖毕ܷov[r%X =o8 bn2)rÄ*;NCErJ/$! <`Q*̚EAΞr<4ɋQN8pY 2j56QޮUq%ΫWF]XrsMKay&H@<8-)u@R5F(CX|IVbAH{ ˞EJIIr$Th^?j4ۗā> stream xڴeX[5kpKww N)ݝ݃wMpw \޻|Ř:|ǚUEIQ^(n rgf`)Y蕁fֆ&&6rr-H t2(;G01q#$ û`:TE[G'z#Cw7dfRڹ;X;JOҟladfcۺ-T `k PjTĔU j* Ul@TH^U TH 7ȫy.'*( f OF 7TS[̝x]]]̜ljnpu: d>N's y*Y c 'I_NQ'۝M}NjZ+GsCǿree6 ' dd0 4A @Ou9R}??1C?f69Z8:9"`ja 3eSQ}^}: '7p1qL"ؼvD3>Q99:3_a[l]AM@@) 7!m3:@{؜Oÿ>oO;[;#h898=O 00vzuA/;ud0"0:K_ĝ mTghhcaÖJ|n@E 'c_v)'w ̬/ڟ+ecg}9.Kc+ >b>?|jҊW6ʼnmM,@fv;ӻX6%#=` 0u@@9LBlF'Qo`7zSU*Fe4~0[M+NgGv@?"m<6Cfe~ofwX[?3vs#?2?;5N}c'j9 YxM@x,п6jwP]!rNn:LךG7?mX:Z@ܒ_Y tNV1LF[\@mHc4U2nYZ9fLkt%TNNpG̯QB4!Xc0v=1+~|Dy 5X1Ú…ft[Dd}@AM,et!z cN2.D6@`;mcK+ `%Tvm ;7ᨲb?)R JlTΓ  P9'?REWg ˑ8.C=h3D 9 30j&Jp-4qp0(Sr$a=Z-Ɇfnyfs*8?$-&NtQDXZfhd@>ywn Vǚ5񪯳t'5 ͲQy[YƂ\ZJR/s|cV yǻXؔ+dʣfFQ\t6cPQ1+4"[+gR6q;̞f oZfቇ?aC,J dCjmbrhgGZ-6?U1AUԁ/l @__~;]Jsto3IYLǪTȖ)8]Ho:%6+=ј2i&yv(%'-*)JlBkmלB']LB} o\g>:DCheq55jﶚ9iY#7<(0+jԓ ϠgR*K'lyP{ea_SΙC%xb nY8ջVanI0;q.Vv.~'ҡ< nR} 'TW:Y{ނG$aVn1:`8P]g`l Cxַȗl˴ePPg| F7rm]A(8j`VmKC;˿@ЀzܖA!oN{VTٺ(]ً 4w3sEdq G쯢LASdcB`JyoBX8qV>io5r6s"35W<K֚j}ZZdM"]<}+ π IeVKHa<۬|Rjl403Jv#m%&䋺]%죽r?<>YHCzu?)0/1CۖNQ˳[2 k⭂mm3:e.A vu{l>0૑L+A!tJ`z.TN=NA!xlHa9Rvk'+t=*bQo0uWTzԾZ깣Y)7 Ef8}fimIZ3eP"}5>i?/Peb[KW f?6MK2\msq\A@-sm +$)/##JsZVJr-{꒖F I~=q͛]Pc؈  ?#GWP`~y0N%zs`;_v qb02el o0Y^o|̛3c^f0l:t$ 5ۍVF KasS{>hQ&^ KQU{Na8,ҟGo)^K Rh(hv]YSDV; {)˟TnWKO5^o'{%]t6bI1 R.w!@ MQ汝HI`ܰyl3r!Pd`ee?>;qgQ@#[|:"1)E[a&E.rp ruٔ&3&|C82qx=|]PZZ<'V דdH'fP\Qj}P*C~qCVHKOJHF/eF,R)[K1!hbPgf\ME) muCx { AM2ryR!Z涾13BY"8rճhh4Qw*Q\4B|XK%eLB5ۻZN n7$m8LחKhI9_ 3+/tR)$GŒt&>q9Ins%6ANAp== vɣ>*7 9_(CIǛ`PLyQfɡVB77޼Վ-˅%/|J4׆*mG j;.XzXAfSc]G'F[(3'ƸJ{oJm6ޡ|pd, a_J/śKIGm)g eLrkNlL ²C 2wv&.V_˸9~E̎L]QHJ+yV2{E;M1;2hPkkk;U%0$X56ioa)_6<%տ}ls"NFY_wo~zTōF0:G2e%PBmj[t2HZr"iOCɨ&A$o(,~fBeDƺt8#4{<)\(>кuNj7ʡBZq/)n(:U2ָKh|z+o 01&ZқOl,Y8( .02-D YVI)K)32`a8FG+la%?-4.kss1kg|d[%ܽV[YezUϢ&\n5m!7 $)䧖WPuCI8e lK$V,F`h4͹Q YUr,w4aQgF~* DHOˍ6jC$*@nއD;ո"AM M'.vO#BnQQДMfWeS 9f2ؒDvl8*&[se!fZbvј6!z? XUilJ*~t?9=eC|0Fl-.DZ!w )p]->U9DZ聪`A&Sz9zKHrfc@b!wX`iHӣ 6h768d|w^H_xפ2U+W2P8NoԭnO4ºniD~reJ "YR87o 16V9KKԼ_4x4>p{dȴ::fqϿ7$;+˵l {E7A#YjWQW4B&hYgZCI9MJE ey/yd+P[cgxgUo}Okۨ2hg\~.(-цEQm<`q?ucT3I?'t}`N] s?e+GLp:̂ι<$ӌ |Vo3C1W-qxFԆQ0O:)O+1`(=1BOOGs<hG]Iٝ*Xړ\p[v?Z~\!7tuzLc?|^#}lD%Sn޾ ǏW}TtR%L2FBD6 Ok2/,luS8O's1 [MA@Nq0,ӦI(Vi*t&OmOv3S,>AKЗ83Q_\t[.%Фd+NrI'\"zYW j{Aɹ1 54C,En.PldXz4<ו G@{IǏM1~[(GuF -\,iH/[g@RqVht.6lSx ~UwCDND8CTC}wWLPf$7"\]+qp6]}M:[k`M-[lBBoYjmH-ǥt+CPCGQx<jr6 "Lk_惬}DGl\zu!%|'WbCXp8^o}s1c@Ђ!D3YOh4B-GL#T?aN?yT>L'EuIKu?S\yONF6c6b6܈eEU͎D6wF3 4}}6Dy<4SʌVxOwdZPqۘn([DmwG (jn0+_"&;oВP łŨ%4??!Yz G?coJ+Zo '0>PE!w6>pwgC"0d7̬;<3<FK|Gu#m]&TMqO}@}5%Z`o6t8g/Y =*}*!py:Ί'W%N:ʉ[ H>`glnwOeN vw]-*]٭Z@V H,xPnи' !US~>էllOy'"(/S^|w-pژ$r_k[62w{MHRu) 4"&C%z,:.ԙx6 1xDa^zE g#--u"]a"x=0AZT> 5fւwHYVNJTJXPV<u5$\y\Vy3ݏ(Kv⢣ߩY5/~{*T2'6]sɬuζQKmcWǜ6 S: H܈)~aJrKeq."\fOZg$5ʴ܈%pjR2d%ɂ]t_~Yaa*VjQ~)Pz*ts.r?D$ mEZ pB "3_>/ JJb'K"{Lިͤ:Y9rh%;D\񻓘d ^PM)*NbNˢ 醟iΖ Hb4'p걥 %9: cou[~5qAUQqL.?*U7>5$Cۤ=0Wp3 >MpHrw4<&xfxl怖ߺL$Dwg"SQž{vE7T|*3mƟyyaOE4R?298{%g<{KTQ=EcDzU2FPr%RQuf#&+ts3Bx.+(P_* iscm-VVUTĿR'u2roİ_]4ڨ¢yNLdN3v<1X"vN5X+n!+4Wl21%STA̤CRɲ3<LNoRܕ.,tK-ã5yA62ӏ&ɦ;2 Y.dIŬTn 4ӳԣҾYX zr,L̍ׄ$6[SXo㙫 0THy~Rt"s腇8O"b8)|& & ~tʟ;3P #jK#CKI6Ō:7ķ02̉*qm[>:g! ̦MKV²*#,HLx.ٗ%KdB. 5]#Z27qR=]6p-qZ6aF6h^WE0kcݪc\fՈ>lm| $ۯ1͐שiƖh''HdYwcnHHنȶ|ٙ 9Dj=`}ߋl3y9_ #}Jv.Es ʎ[i$پ {.<ǎYi'yGIyW)HأUBqh#Ƭ:xn3}QVNV7{+M5iڴ7JPЉd]`=oiR ^q;OHfE7?aĆ˾kCyS[ŠC~xF$w^ܢjXͲ đr`IH]8T~4yRLY(-Ay .ԶV?\^|X烶}/G;3w\H3⏐Rm7^.=HEfa5q/ ^KbQ(3}MnE ߫؇f^:=7r^ĺ:5fk8D>*VId_/Dgh!([ƜhtbP"-|'MϷW$H#{F(c۲ڧ)ҷF6îq'Dń JNYh[CP3w@(J;rs*XGlGE#洋4k=X{٦Ht9%0dnrNscƐwW8[Ck]rz #$Ro*ϥ'R<= j+/yܪ^G0W4{Y5-" 3r.%QNn_ijsx]:I-#y~_gsxJM;fϺ4_>NdPd"Gɰ&"`f 슂CC 97C9|,XCJjUnvs8%By< Pk-`EbS\NWՍ=;3_%< "1K"7:C$FڭfE.,yO1p?ZyNߓBZʴXVҼi:74~ٽ]D&t E+ݵE|",r○$漉o E0Rgtk_F!."Kٰp=8KG4]" r\q{;F<}>ns)qRSoC`@R}ٍ, ŸB-l`jJo%z6I(.~2ʒgN6l~/*jr7*zC]QsV_=&/n?>VDZr&f^ҫF_rژ33 ߋǃ²`F&rqi *Dnm%\%.ӰeosIS_Y`{)%ψG$DTl2CƔ`dvA}jVR5QhG_/vuެ"jWBoOo`lo֌`Let{\DD"&.دSŽ!WJ+KA%Qٰ՛E /{o$J59CbSYz+köQPǖX0_]%dˬoÖcvșz+j!2$zOTS;?jϷRQ#oF}eSgLQ:=KL^fI o_jbvҷau-n!\ˬ'NsMh`5W/8HnΗ矜Y(0o.wjձ..CNTUFC'U|Ed,]ŋ_"_Z@Yx_;(nLT2 7cmWF 8Y&/DMLÊFqpK Q7Vo\ `HMC7R֦xlx CiȢrnN9pX&er K , (l'зlFl}$xFAZ65߶qq `xfv$a׊ S.q2"ا$9ee4%r8SMBޠrOQ]Z,Q\6/V 4hn >C ʂvS1H Iu27p_<1p#LIQU~D I5u5AILM{-jL}u],Xf_p)Wց1؈Q &!o4#&r^Nсl-뷕H2 B>? LNW9Grgnj8 I-X}y/h z\鸘BS?53^J]\rLLˀMF7#ߣ+Aח5Mm&M%xeYaMHExp Sb.P}Pto3YT{2|nQKxxj6ٙ]r^zFB :?VLS>KU 29AYf]K?wj8O1/A: 1 9GEZ.2M)|3=<ϧ9OS|Uy/؛<ԻH\'ˆ[Ÿp鐀43Vѧ児VN9po7x$cUgٸ#p>.b.r,3TO43'5Ty.kC]*5 ]:ݱcdu ]l[][̞c6Ql7  IDCB.R}ߔ";̽\]y!Qz ;ʑLԹrHKӻy@xuvyr`VD$=Gɵ 2]OݤxG<rP $1>}m ta[&~F.:Nxt*zof9운I!gܜWT{Z"$_p74;p|+4%6}Rhmu3/^ nz!@&d-^IӝerP[Fm,kՌg7y:F|w]_~]L0C>#:62~ t#L9QwH'Q1?_( _&"52Ua";oj4);3m}|(r|cpbŷ՟4cU}h(TվDॾ9!sx3?*,_?~ohZC=g-|e~+8T@ȫxpϷ/>+e| ft{YRX5Zv2;|im}P4'.VfEJ,),vl?MZHX 5&iȵB>uH}\&VVrQY+^lBx^GzȵR/ ȍ+~a.05tR˰\ m#:N1[-S"+m܂lc42touU+5L͆U.t%|d1N$sے[quMtժLM }\1n=Pd~~+GG'Ґb#"pHXyF`;OD JaHeS:7Te7S Ct&Yf05ddC149H\z0_&!j~ Y!ڤ➷j!,JpqB2U;Z&9Bf_fMGw@;՝m3="P2=~^Ejp;4\i'gx$nRU {0˲=οEJI~ݰV(H&+:I9#D}+F}Hِ 6/z7)eTlM6pu߀>I#QI |Ld2h(?`5 I*CY70t?lJ4VPw s~G hx5_ceAS muX/~#PC[EAQ,9{͝Gݸdrfw6Y$ߏ&4SEq!2*ZRy ;foEq,QجJ1D69#A; EKz62)tXp:+>YQ|erG$jPC̹j"P ۗ퉫*2ö)&7\ K4a.a.b GEݯ?YuHէ|wu0[Uٺ@I$eS,7$`[д·3S&Qʼ"R dKȅ׉ZTE]VCϏ/{0f8JS[P'4@ݘ<7[5AHR:']0/ 07Cu٨d90Zh#Ya5IkP$OAJev &]n6|UG^;Fm.o*$ }|8a=hAL7fD_)v8ARoɘR\\ÖGQ"__SnyJdR+ϩ mަ<ŒGЭYJ,{CҮ I5t5d=m !зhX;5OHH}xh[k#Ϋ -Fp4 c٤}W,p>YFZͻ3?@%6(Lc:NTn֘(֬XP\ j'اfk -JgnWZ&[8:u4W_de[Fe'ӢDBxBd, ]ѽd FN¾"/&欿s RoEgDӠ{CVsc@BH]:lVru7=6Ԥ2i8r8+;? oFWP89yљVOL`nA.o+"6P"i@E+<_d$0Nj  Q9IĆzόjPXT%s'.^nʸR&r~'9 ǫBl~h[7uo8・׵%>,xDG77J &$aoPcurTqd8s#}#GӪ,W]\iK[Fc(+7SԸՌ27 .p>CMfBԬe%W}0~:ʬI)W|Ǯ0wK+9(o!Dpe \ވT3*,̠IDW[bFI֏HlvÖM^k1(d$&ɺfs,8wݿ1N# MiD7m1o 2#SNʱ%^kI;H[UĞ s܏{͎SI,/OH:*TH*MIiQj )zVHeLKoX `Ȋ-z`11VRRjM%6G}d  !-Bm7O62c$@e(ڨD"\3 u<>z6.ԗ^M4gYKZW zKm5*[;<Ä(QpAg8@@[EFTU _yI m5͵;:#l*5,¬Ͼwg68Ikz)^9G묥CرŜn#o{IVm:fJq50bIX&b,QфXy]K}f2wfY,Ģ$rsɡ w8VH>y$ &*rNϮ r>V 4H[ܮ>Y8DLrj܇sƖl6(R ڇةR fze lm~MG$|X;;SleK+ ͗}8fnėL$,ZEx{7t} (;:n[3+/[ Caָ{FTr(_,@iyq6dn %L)e bQκ4qseuto{[ץEIkVH eOu8"f;5I [X@I"?%X^t6K3*MA^6(, #l`GIu^|Оemo%)%w~ʈ9C y'ud /ŸN4J瀌}4:fxw|urm$XE-ĆkOi9n%>z4Awf$N7S9P/*Jӡr&gcuIg2Px^ .tp^fCܺ,ND3pd&B5'eY.Yf<&FۓL`X=O1] Ve)Zsj4FcYOӀ&S' p!QxUcAr?TխӋgֹsT6 Rt!+9(QTY8ݐxBD>!@a!_D3k\jc=UBɖ?un(JD5_ ǎB7̚t5ڢ[LO"[wfvB~جYUƷ*:׃Ѓߩ ؽj#ƭpB3Wo#/\ճ -˝55xUo8MJ=g x)(J%Y$Y{ZwKY n"1].ы`c2?nsؠ&>I*څ:)MCX>1C*`ʭ30@8?혇љxaW|eVSlaU| 8Hzǫ?6L}-Rԥ׵dԵYa*!fsl0M]ȹvOCe1ep7pb#]?>Tzq|[i/Z5U\Ol7n%^j6D~dcn8?BZk+ Uʖڳ?{UEd^"XE&Ҕ&9eegI /𴫿FowЩM;/Rd/ f38n_ fkʧ-ڞn-LlaH X453)7xonAMz,o65U$D ~Ŷ$N\ȓRu!xj^t]2̶p}2&lC<>6_'i( 9Xf9GHPrN"kks\ qu(o{U˙>BN6ős3Q< .Ն|GAbEAK9[Hɼ.&iU}}%W@kHo6VFIl>S^m 5)/v6t7fRg=Ei$nW!CR}<# AJ7[ުl}xt7$ 1쵗rа!h/lv_*h~DzVG W_I6Hxjo Yc$wَSR(`ƌQrcZ֯eD]:7>\YbY<3,l`~l>= -^1+_8KYy/A&('Su~n 2󬁓uF3ya,:,3V~&MT!zONعL@WAj,.NըdC@؟o$ʬ^WGU QtDjRF7<].9^DOA) L?the%~z`_ӂ/OcRr*S\6\MRs$gǤX$5n9ɲzK,dUͺD8CAqV[?spRg~8O) ,U&2ꣾOG#QxMv3`I8ԑ>t`2T1w7>`_]`|~so!UX *TQ= 3a̕*FXbgCIa[C,5xͼ&\o6SH R[I`n Ūک|lBxwz2}?E/?| J~]8p+nQ7V2ғ>Z$ li&xq២jm5/uVZ[ԺjYrLa`ՆOP*yu -m@A-AxmT?6?C3W)AO松BYwC<+Xdѵx3qH4{hgB)")5.Y~$lӪ pT@%A !)I)ǫٷ]9F[}#>]ȒѧuH=Er{L*}^e@C+igz,L2D.OU(&ysa^jnU s l(@،sLn`{H!y^)%D[%SL5Qw;q?n9ڳ"Rܪ$g7CvUu0#*.)e\imRn F'~U )}`5P{&Y[bsТT"e8A0m-$)L-KnanBW ie$`&<+f>UZ"$3C:T{MCb(#_gTZBq i"[Z5T lIgTWU+Q"HpY 49ҁA9KLO.{4Hf7ue$]w<:Su\޻ԽfGw?Z%Os:db.\!%GT_ۈgq},dݦ% 9cEjF4E#qL>#2(VKKh' M54JV*xwML@/ K1WO!vc8Dr6,H_p?n[FF.Bbj wuFBvןM((GwW~H}2.DXG* 0hc+HJo{n+5߆L"u !ѽ:U>k"甗ŤB4Od faпUATB鹟z  l`貼89f9DH1i (J\u ǥY=O#g~}tYE;Ꮃo4>~l(!"NEוM(@70HYF@%BvO0q'-bOL#yTl la JN'h^l*-J/K9Р;sH]3ȆFܗ\*g)35)+w ~$da xU6>=)ag2j*k hK;:8t_?|o=Ji6Gz5ą'֮,x0y"{m\%=#~=]"ds1=c>U/&&G+OH8f,!sjQX(,ݥ+Σ%;y${ #ҟp'ipU蝢k8BymPb69Y߭*G1h^@5=>{&ϰ&RZ-v4GF]Pprg<"X*:UCjϾlA+c|ZJ [#-s: N͒*exP84d C^ۉG/CI9lo{?X֊ĭBTwϡ ؃(ekZI[o*yÕP'`"&?,Tfޗ\{ >KnMnM{]u}ZmE/X꽚*,YtK A1mJ7N7C-6L-&X 6yRy 38p!G<10l3*VAQ! hQ=L*V&q:/3 Wn#,r `LArGV_YBJQA;֯ƌ\AVsm@QUS?qWsfIT à*:4R3c;ZQg}[1}HGrScUAol;*P;56M܊vU =hB;RxIx~R endstream endobj 162 0 obj << /Length1 1940 /Length2 23359 /Length3 0 /Length 24522 /Filter /FlateDecode >> stream xڴeX۲5wNNp84N][p uk4t̚Uo )(sf`ad+lX.6FVFffvx QG%N r|6q~}`f恧H퀎JS@ladPAN FNjEdhin'ßHE2F& 7'kK)@Qr{ZAvcdPjTĕUʟUhۃ$=@LXAUTH {oNPP}n]^\UXUKQ,W埴ō]A%P[8;211893mjap9Z_6 bg^Ng  @h$[i^ww{/6Hcahgdgnl0K 4  'T_E@'ӵ1r3sqGm& ;'K'g#f6?ܙ_2yai qUƳcWǎ/?x{ۙlmY;){ALoc[ہٟڛ3Y:]o9 :&L/,̌l>fx/'#W O"x.{ _ѥ@L&kTidg03)[I\.66 F@mhdkieÖZhkd_:K' Kwߥ[.lv6kKgl{}XY_Vҽ 5U%%߶Ndjig`9:y3+影M5 wq\(7Io`R_`270LqL@6d`5|k`|nߐ=?'Gv| `r|??{"CVV_?oJ϶k }u*Ύ k[?L䍜-ug]#ſ"" w/23rsXظr}? M@&RCJMBS0c hA-Meo)ArRz_ 5)l^$VLޘ* },.<]Q- ]~ɯP{VLzK\ @mH1u *Tg[K *^;T;#fLQ2an }w':7 ʹ"pm]5c7zfӧ+H/a(qj 'XZ7ԶǥmIoY 'Z-(zˤA/$k)T0NtK?#=X#*ޫXkVR퀵.}9E僗lJ J3Wq""U5 $2 9nT(W~ʭD\ 1=Ъˤ1DGSZʻLCtz֍.W5ӆk+ɫe(X7i9HT;?SVV#jNI G5i۩W@Z)Ƌ3 3-1Q\S]8+w*r4@/OuFD֙\\^P4ͭu& м=ܘ'C2w!oG<[[KI{ 9dޘo>g ,ٵnM7'˖,#h K}=g֛XNTi;p GNloH[Vɒ oE u L/?!SQ M%"O{%D?e<=~j޵dϵl/!Nu.2 ɞ߆콟6wb'QKrY7^僎&½Ebf| M\ L?5@X}Dа : m?2&bigO Ʊ엌K<[S+ܙB/tqfD$;}S?]-PsV3inHv"24f- kAa7c`PKnT֨/q5B^TZg^{ Jk6醧FbT{aiJMd y{R| =ځ5X*A$2ځw8zY!l'GfLP-or 6Tzy.֚}bUO8;ETn쭓3ko);ηΛӕ9p2TQ F!,f ݴmInt?dz!%akX!Ý \Əcb(K1P+IU&* })n{vWpTijؐ8%WCIX܌eLc<;q 2t|O31BpwgzN I&Ìm B#nAL9R.gɝX%:agj(|WȊ?Wqum4cHD{4Ijxa}ja [govGd_p+yQY?E:W#[ `0^fgTAG;*Ghl?9ܼ< r|G`$Fk%}VQW$)ϕ,,N;E|7⟚yK`89܀ma shwzO Iv.!ӂda}N;ft~J kgp ?_rNW&J "x%ﳋd?XPMJ3wԱAeJ1;v$",,ڼs `h#`w5c=tcp[>MD EUbuZ"/dy{(y F@|ߐZSs3P>̗(ުLկg>moEnɓؗ J*nþ$J_r0L>0mLX Gd4]Xh](>`tYnXȾ9L`vոJ n*FMx%WHsUYÇn1t)$:9 ~Çfpls),?yȵՀIsGԗz|wh3W $+7R7Uw.eԸ*&*$F 󼛿Ԭ>Z c^b8+Vu2K]>+SK7}J׺BGtJ&/65XY򀯮ِ=rƵbL#Ȧgbp5-u?.~t!CgF:TY-D. ˦%0pXpk@֏\^.̲ ꨱQ&:a yq'u~kh"s{Yg]Wa6Q^ Iu:>u$)=)Fˊz(Ph4\?w;M}.CV4[w\~5Nv&α#%c5;'ԙyДO6C~~՛+ty͞+-W`+WhMʯ sL&7J4A P܆~8<= ]c hQ~Ek]ثyZdH ^س}$]YvDeܧjh&ږ`aJ009g|_Z 1ڝʺ},떶bow >a' Ys/.4FԠ:A~"l} 3EI 2\,~!eqhbBws<=E^7;ǥw@argrta9?aCdʛlֆӥ@c|HbUS2۪nkᖾ5VLj32j0т.N̓|V.Dz(oD̝ڮ> NLn0l>ż5g@,v87 Q0AAH,9u"ҫ]B=^̄\4ulr=M0gKO\%fW+wސ.h%[ǽ<Oej?mg˵? v B+Yp14 ATT"ve||+,0%HQn.&MJ7UT:Fʾju!S;Xcp_ݯg<;*O2'ji%A^|E'}Bpy5),B?ӳe9OT?&VIP?<_oݣ@pB}I]..&׷4f<ݪ2AJS=GHD4[s@ kFDVJ 2+$+F!\0JLذ(M>Ǚc4H= y{k1knj{2ˍq*w>_7$޴*VN^W_-|*ʯeZgv>!7Vo֙]=J"GFeOz'KƉ'bʶofps\{\[Q\SF},<&]U Da.8拜`Ш_BX મ&1R/A~5Q )zHVst!IK1w>EzKn)Nf̤EaJv%RoDԌ*r+ثM{Q57=7v@ } a9oU=o`* :%wS5$^WgPK1Pz}JnOVgj7៵6u-]?F)wUۣ!J!W ~5EWj:~Dto88X\.ǎ`Y oje|ڔ,DsB-i¸SJz?9å[Q̦6n'OȀJuM}3+z9ʭୱS\35ʨ37AqL6LtNc`!/1nӴ^|[6P(΋ǟ\Oy{EwKFhͮ,xEk4fcu]P>WM1!(똻T@ggӲe/.dx}[5"gxk,[KrIU]d^kB6]蠔77t//1UCqoHCΙewx|eCZ']zXPQ2H|n,#}:ES~sRo @]G$XQǷ=_ %To N?ӊ7O oXb9'#H7Y|mO^yǚ׶?J}N::r4ޝ$y)7bET̲&sz bƛkװ3!塼Q+ћ ,xqFqGۭh0QZL?? ί8!zDeriMz{ vCyM?,7޶eq eP[f%S\zeQL'6emŲ=׋Aՙ yn)LTj``č !ocCCKXtG Z]B%ptfS57=Qܦ]9},F<%BRcJ=9[ LŽ㎅Ռ`诨!F~bg(rMc13\So( ~kD ۦbb*Y]1O6ygnփqys Mp2Ce^ #ER v5xllrԶzGHҹ7bMƕPiKx8_u7GI|MG~fAm6<U)QzLժ(Nq%BNGmKNBBL{|D P,.o 3q5tjrAxW@rHQMpb1^F?.mڄ8Yr&03 b~HxpU^xL=/VHl0's+Y#0/_oBV#v4Zܻ $sI2ѡ q1\x.Z"'(v4s23\.ӧe~I\קOiGD,:^X$"ubXUZ2г m %T3le Gkma{`N;tjMh}Z*CB$Y~sC, J+ץYl T]ԓ28 ѐ>n5On5/`!V V]sXA|$=s$6Afk@ !B ZXGfیZi8V]*w'pFXXp"L-e,=]MzCdv M_7jbzO!r4مI2d4C@3/'87َgDk<:&{C,knım2}݃'{)VIR]p }dL]$!ٞ90]*53;Mqֽ`prY%#Ȳq⺪'Sk]ʴM;sq-=#ۍNlpF?0,S>E jp#О*m#GlU#C4X`I9{{ziIvA>tBbHjЩ*Ϩ4 -LXxM#w Ts5?Ɵ[Ep5Dbl5V|Wۺ|WpHe >HzJ<#c"i|Zݗ嗘^59XT)~w2 ~$sֵc&0btpւ} ?g&pUVZ˴8Z ث8 ߿Lٔ-7ThF_ޖ)AE.)>#$^ntg\TV0TDB zXLk͆u 9d6%YAjltq{;po;3G RgZk'FO@bΐKC:7]1GM$Mti|5h Do‡v~ʗ(o5zDS52j>$JBX 38F:sLi7/X\ġАsօ9(_!6!?ެIh }FZPK#Z>|4tʄR|C.v6J)( ^I92׀(UO:sZ 0Y1GU wddkmuwpsDŽ uPFsY& "jWcP!JESv?395iW˞]fbݴ{-Qs0W;0 _Wts ܺ4r6}# Nx,ҕ7dGWh!k4"ppB=~ʰ}ӥ]n_O[W24J_ D6+FKꛁe9 S^-::*e*Y87W"}e7:4z])X+F8`"Pfdx;v}76'H*J)"ۧ1)`$UyFH?Nmjqf|X/8q?B,~2d?aT:~ e9JPv"UYãs=gz\Ro2֒o'`=.FdO0kڂwq˺q:-AcUX-^?¼P۩H1ĵl.fk!*E>`\xP\++A >XƯE2dLMФȸq]LGYy.&q=hXz=e RK~Ux1 ?>QSI0cpuDf򤡗Nŗ7GH,HMWI?ۙz@Jd@KâUTKTCbUDkNVmHq%G;s5ajdM#4[ȱLԖ-4q"ATݡsE=O~%DZM:iufm1j%J]!T&rLYW'ҼtZJ=mb跏U_]BC?vS+B8eؾ1di9\T/S~P[goGM%:ٺ5vvϡ5lv#73q1答Dj%"4U ~4n?.Y5 4b˭1>LgqQ醡$HFϣϴӚ#Z-hA=`!!V2e>CșqÍLL[` }JBlaY:e9J7@(RrFo]:OVЁ H`2;߉g=w"gظ#va -dt]쌏Y$7,퓗򚴡͡+hs4=bG~V]N!goKBxkdMNyʓ#j\u_ftvsM~Z|@LjX\W@/cMCI) #̣>n\iXo䮷:"sµ8*D+\>#mX* f9rdTڐk/v X97m9 5~Lb{DjG5-]/7dv๬q(qQܯ?=¼eQ0)Bjm*+[3r ۃ+8kcԗb{4 n{xX<#ڽ@KR^Cܛ n^-VBpvFDƾ_XնmsBqGvCQH+Mmlz7XÊ!nNNkPݍb je͕&ih/lHnf Js'6[8Ld*,ۇg_Nwd% /spɄ}ۦU(6IŬxDvzzǾ ɟȊKg 5UJ~aɘ!6d!(:/|c`*CS$=n*$ӣUy$gG]4tfoU@$Ǥx4@MP(a>hhW,//lIⲇ~JGQd%D FKIaP"𪥳̴ˠ'/mџgnȏS֮W3;+װG 5/N4O14^'=IaJ~g) p I5)?ʪ5 Bƚ6?^Z܋rQiX͍&8[pt/>8ˆy(%xj@1&\D}\Xnw1@0rHpF'ӴAs |.]6ӻJ &^ ePV)s%`6jyS1pg랱^*OO)$1k0k-[O iUrx@E@=}>ke'M;pK*ZEC2P 143OxG%d-A:iփwoԾL-*df-g6˟FRbES7age]VX )"vKP0FDH=F8VW }~BwV #W ]߬ӻi,UO3N>h|!\K{4,N5gӊwOP=S9\/_t2% Z!>{<ԙ{[-y,>^3oeENX* -5`_uȜGpi*elI?Bnø}$}Qsγ'}f^`g*5 laFl312| & }^hd58:$&39?_$ebf7NtWBԜzJΙBt26@uƕVjvannޚ20C kCט5WPQUZEHN{E9*'R*Ϭ(5!3PCuz)׸~G3Q&2ȏ&o̶-oޥe H_~3$bgga+xLyaaXrhd[Z/V^V,g㧗$ţ9ZMޒȫ²S<:R;&Wom%C$a.ǯ`p>Hۂz=|#5Tb;&o?N#LK{a{b_O;7~&w6,xk21옂 mdF$tF؋~|9ݸJfa)b8^?|ޑ%Tؔc#EP&;_Iu]"2KzkνݔMp3⋌CX&Dp8&,OՕp15?,t?CQ.|d''>cP:\+?-˛OCO#gޘVz48^ưƼ@O(\Iavn]v*QxBt.[3(ݎ MRLn"[ǴF٦JgTB犫9ٗ/?_%?tYrbc`1dn$73I#D#W{TWGOˠ>K.lUg>Usۯ0 cRp7N (q>}QKVX"yxqVH~rd.9(Y"/sA_XdF.;Iɿ2ؚҌ4;<=œ+.CJ##G/wR߯˺5ƿIFEzU_j :o=p:?@;G*Y~\G>(kyYȾTc0G[ v14Y콃&aU-m9oL e?jf--fAtr,;Ęܞ&q#s\?03 "1)o1巎1Gb\ cѐ/`8\R}SقLXJ$Zu H(tekx@ Zbە7I⟇vYҠ&vu͗/^?gH O@=powyMTN *id =\;1lB1>`H~N-(QxQJՔ+\XP M[G~r )jq4ηrE$_N ?h+.yߊ,Kej1FgN\s#jɞڗN~]>Àe$%jC&_E"1.Ͳ־|lq͍ޛ.+?_QLd LR&7'>L㧾X#"c6 4U7H,>|!6&_81 M\&dӫ=ZP|lZ.z4Uw喰[s$5y7M"#W&6 ,݌b\%+-ũW93e;êR}EQ'A- KʪVs,Qm$VʧZ2(.#enɸǟGMҊ8Fcɋ<<3= ZrX5۵[r0}8tH[]vOex>7xFz!@9u3J~~$e.i˨YE}Ƹy-XvĔHʼn?6;/\ϥiևewUkJXٓgi^sCa Ko~T[A漦bgMe;VVi2Z_DdOGq-m/xĀ1 ^9rxB]N!Ndy+N,[isa(pu{]>!؁vK6 b4,ѩpf x]_q!u'Aq>Ir 1y- ̪AI%kNt+4&}e<ib^E}O^aAzKdj[3:$oDo<Љ??gTԛ C=MxC9S<>T: -mMQyVLtsפ!Lωݝ>G8(|RJ CɏGLSJYE/c.Uӛ/,1hyt)MGՇB˿uBɪi D&ʞ30'?]>~$4"D `Ze?bq{T[7vq,]{s!>"(]>tbɒͻn%`*8:kNofg*dhjɖ\Q$ %*5y"$>ָМuD} ࣚʨGUC^ )kƦqHͣI[>RMdBz&nipy+ gj3ѸW7vR󳱩BV.s*դvg,%.\X6F)x/ͮ戺1O:瞩;CE.*VYCDPsfC:|C%K`.nPj]]|Z=0HIՏW<W itgTcܚXibod38QJf.-)}o,-&;H}鐛<*F% 8ܻ.%VPIҴ"f45!1KéV92PΈ0s"% L:'$-z=PĠT)fEgII,Tq;.]{ \ 5b/Df >uTnJJLSBV:bد0KGHP%зk}rviH:Kꋗt-OgG-KT$~ȐQ,ϲў*΁j^<4V k{̐NǼǮ)'XVa&l/=g)u!62 K'v?4rBqlbQGŸ:.pvSyGg'tEquo&V>l`RK|"0t,bHm5^X QiC} 2v'yؗ]$FޏOg{ỨНۊI;Ro?(8"⨋374uNs6-6dh z i43> )܆ FBFcW67Igwgdh%"퀝ljܘfg(& Qj65 SSj72܈i-aA ՚Ajn鲖 q]t彏XRlс9N&2'Ay4.Ϝ{ԋ(yα^cEk?qk_PB7-Cf˙H[-J_\P[;ЁSFh Jm؅g}֛^?) -iP{yG62):Œ]ص=V@cf1Њ5O_p ɺc͇r?I=z\ U%7`Iʂſe;9+WDd׏3'P|&’14~cr>aRQjzF_Ҧ3*;5=4Kjiщ}*yN=w6ga5sce2Q}^'Sz{ޢLs^.et I`xPQB`O:..R"h9Fg)Lu@t͗ QWy"*2ʤw: od?\ _-*:V%I%iT4^|H7㼧 MXgb1Q!|O^kJK9_3ϼc'>xZV\Vh*Cdu1eG VdrRQ?7Xw φvb"in4EKȄ+fH6<ɵ'5LCKLU5~:ҙHURLZL!"^]Q͔hfIpwb?$>K !R3*[;+-tuo;aPIEd4"r2!=Be1BIc9TV3߆GĶ?9a`Y) ]2[i؂Zɀz1?Ⓘ\l) ?.]g~l2/+5*Awce}(A* 1 {ImdQegUسS^Ri I0'^l/fn Aиd xӋUCكPvv,EǸT] Bpm%~mKyx"1N;|:Rj0;-i-=Mb۾wvCJ:͸{RB ~7Uƭw6w'@gSX-pf:U%=CMY"v;-&~ǴUuྭQ -3j2Q\@D=-4Ky@_ 8z+0~ї q2㯟hPZF@d!NiۘZSB=^(Բ٨MpvjGTs ǖi.ΜVY5x%w~Bo7NKNvq5xp 1A83V}P cv3  NTr0oP t(_qY$D6C'6in{jĨ 7:baz-޽Lq%ojgXNU*/bv l0~l+Xs$PdC0>[AB;%!ŏG>D X)Mh W]OamJw,~;[hmh0@ּnod2#JDtUvy] =&g$]v䷼?%6M]I $,֭kv߿DwklTdm!_\n |x_\vUy?`GWn_YBͻXFwtb"L ˂oDB{B5?y{yM>JipM`/I_!R=3wMtva37 K5k=kؖ ٩-F0bv} &<8|-6CCA%Xc"фA.30W;\GkF&{,Tbԉ]"]Y5uC6 s\}yK}P~{7 5zoAAWN\VG Y~wO3\?ךĪT)I Uk4?q?Knk4B,qF-J N;VVdFYӱן4NarYd@߭Ա :#߭r\H"`dž8Qtnc}餯#vUvmBTvBoc#[ETf/p`c_)4\Q-8=})^.bcBqEĮŧ]Gh> F_wΕcw8%U]reܻ_Pba>, j!NEK?~.g+#`Cj+W7:}"xAus ^{n@ʺ&NBG @_=ֽ|"hP-N*e{ֽ8H%mAB.)4YTE e2R/xΎ}ƽ6$Q׀qh;%D' ldxV:>X%1{CP@*^v,Nax CgdޖW@k(jO5N{1ZDE@9H3i>:%7/kc[(ܥC3bkLtTbA<BR5I^0Q>xwq}r(%X4DՂAOxވVv N 1BP\hrz);hH ^ GJA\GROG1dŊVI0<9Ο5{UX?@G;*sq|C_{{DY7?ݪjoN?4za i@z\h!8 ln`ZEa{č8ϘYQj;Ju*KsR16b͜zhPZP#X_DGPq(a%^|8N`e-WOU2vYEdS yug[ ĺ6#q~1sN 0!렫OVSS6&tuDD'1[@@eFwn‚~Ԩs -@Å=3}_n;k\gG.Dak()z.ʈU$!?*+ p&X0~؄֭m{ G$ޫ]Tj wpa_]i'Xu v ?Q73>Sej;ٿdvk-i F0Y_iǧ R͆E蘞2VH)i= ىR7]"vL !#)N܀W%ZI|B04?Hc>{1FcϭNccɇTC<&2 5.WVgq#ZiˁvNӌc}hV7ӖVÌOӒЎB/R`>t,8x&'U1w%NHq긫Ϻ&۵xm^c`-'y^oϻ$/ACޫCƑZ,z#Iv QRwP?,$4 (i6 NA8P4IDzJ~ۉb8E8uronAVdTj<]]|8SsP@Ro7AO jimϝiGZjg sZ0o$݊XQA Rk`mg~ #bdOoU1MEѹ薡A l dTǻ|øCP쉥ߪcQĹmN @9X%f?1#r}HNBsTR쩪O [Br5HE9b`WyJJ fĿ*իY*.$>oDQ n 8j3 GGMD#iGśˊb ]jt&k3N$gp&YX94!j/l֘;%`kc|gJ";YJvVVriP2}Ǧ~_&o|zhw^+d|f2C3FO;o9 63YIC:K)ٴt&ygoF|پ~Q8-*TIqp",S.IsWthvޓ\Ҏ yO6 Vݰx@?AJAhXq gBSﭶ݄Pd"yXWީ+#:'N4/}=KWiYe ou%j'9ITSIƵ_FJB~! Hkot$m1{rK3,@NHC.ԙA5p0Ɉf*2&"XYf Fܿ8/_ɮċG7m˸KC gx^QZݖx@b9GOIowMzl{:s>F*?;;l`BbUA+m2`Zr"31's ơ9'_{.g:a<i48J6A$( 'K>ڬAsQ6= *2PHPm_Qd:{7m.q endstream endobj 164 0 obj << /Length1 1664 /Length2 16411 /Length3 0 /Length 17402 /Filter /FlateDecode >> stream xڴuT\Ϛ6 㮁Ҹ{pwmw! %kpy<3szuw]UvwQ(2L {Ff^yy= 3( ̎@A!4vًy܌,̌<)=Cm0]<,j% hoae9x:YYX>r107Z kljrwۛd d0Zۚ@5@]UBE JX_XTԥ" j=@J]UdAPP7\^BMDM[I=Xn@'ge 2@5wS@mhr`t3d:m4 p+|2;IehGЇhߜr8?_쌭]..toտbNNko袠;ӳ5v3wu_m wrvqWF 1G'/ #)r 3x7^7;AO {31jg ɓ mcrQ[ٛ+PF?T@GԒoWW_oke x;.N@_m S2 ϿH_g|N3' hrfٿՒtU0R[GG,r)YZe\?x/boa Tg? lt4:;Y1?ox?:-INYJKC 󏗄) 0vr2D``+f@h`b|\]| 'C`G0)aSՙ/LS1ZU]@6@M+3"odA뿿-* f``01s,l<'_?0",X6INCS0Vb j&@-eLm2)@_ykQ|}[oI5S1G`Tʔ_ ($9-.alMh%w=ưN_ꕷCcit䱈ׁ8m+D;kT5* ӅCZ}_T֣cB/A,haYbS8Zb"ΤAJ~N9R_ 1B, 9-Wt|wЩ"Qn{ frX+nOSsf &Q~GHWTFbp}hg!!]_G qXb~J龎6HlZHK='!ēS.P0?*$ӘW4jY'hWL@h|:3XFyh۱Vh`2 StP57q:Ow͚y&dm6nGtfK:n{ia}oA c_Y W嫉 83XGlk~CpNU+wMs\Etdt- F[ n_NG߸ҦJ0$>,U7bJ;^qc똲Bf0W&uߞk٬& |ͣG.- _< #}Y-6JJb8 ʋ9DIdg{O$`;P*ɒ_Pbec)FDu8~N׍O MǧAưpN8P+\JW紭V(_dB(6PeT_aYg 3YX׆ oN#{iGk_䊫P e%fbqSOlsM!VAswЯJD/~n*Kx̥+[:f%AW:D6(/ۼt bwa2DS0|Yߞ8OD_z *7cݘ>M )6J]@ V܍q^؊w]6eOd \ӧ=7coʀw#h1i Sv8qbO=žO y:>{+5: o@WeJ2q9k <`TUfsx`ȀS͝: N~jYK䴯R0)ڕ\F\ -ÞS:SEyڠeB"GŐ'$,]՘W3 #a`2?2GsL2" T Ү?Y@W)\-Ͼj:}ʇ(ӪA.>=g 6IcY/nN6C M_Tndxf3hH?ڤ<3($|b Pd>]܂6PZ$NgoCg_o4Ȭo2dƣV% ئ\qg@» Q'=nqx&7-ؘy21 GJ%ʻ󟡦:y{D`Mo_ՌzQu؜ZpQ-+UB^vsO1rL,s`SB,DS Z oP Rf&3d]0Y$zM;=`HCD1j^@(4qlPՅgy {aj*ACŹ$VroQnw`%'5U?48w1oTп}#+bY`̑&3uqJ".$1yVt*sz46֚[+jC;IUmȐ|SmwM_m-F*lx$ ?qJ&+1"CcJHLTѭ]P֟/GLքB: ]&s籕X ZÝc(t^*>YX9m%.kŐ WT O¹fZNd cx -J{ME;/ks2rD.縛(9:61u7AUҒ~=%Gq=࿖^>w'o 颖Lu%^Z<AK1g㏨AʤFouQ5 >1wM9bֽZƹMazbJxW\b \qPLrL^jnK8;ZgÀ[Gf?\"yq_sTܨ_Kj*qda!=*ohiIXۋr:,,=V/z2]2{jVvp H):.4'a!W{__qai\cz8#A>VMPs9$sW'9F>lMM<]LVs%\|,vt}>KNyŏ?55gxat8rL,Tgb|'iZ:/LLPx|BY=X{Y'-uPFfhmzm8+"E;tUT;GM]֞ʃ@Fb_،Lg&TȠcp]p-l.w&(dzŧ[ 33jdcR$B_9NX W̋|C죿m>NZ{Bnq̌Xr# ( o",~?)ֿu]LP]in99"] r_D%~9a'VHf"yTv``gp]tԹ9gtp S9PG2lY Dg3rlq"ncD:ڀGV-Sw7iJ=([3߻*#^0RycŽCHCwovrUqyΒā^PCjã%^;uP9'$ӳ8!<败2-g("ud֘[8db!̟ؓ0,|Ǹ {狫yA7눿80`EۨPʋd=)5lDuhݾmq-rtB PJ5Xc%l&pZ!L|m]uϞ-B.e쮍.AhCZ9|&šR0>dӖЯPX$dWit^th _xkrng TC4 t"OE6/w>-rNcNZh،n3ďVv)y.=:;-(`qyqڋ |ϦB`Qnr8b,[*N,шKLhݘ1f@!fbk}~kCMoHlYmƟu ZJ۟믍7/xOh-8e5Jn0Jč̏eE?BxTR{  ,w"KuO.G?4'Sn:rM{g|[fc=Ǧt`lOX';ٴȨ K563R1R*5Ԇ3H򕭢O YZ'l-E~{;KKie0Tg KvqBj{0Ѱav؜Q]Xj#HޥWocB”K{YUi ZʼKbOujpxі:Y*Eb@bA;\G>Ǧ:Dr׫}=IOAU9IJϾA!n?چ$44`Mې{GUdLԖl=!{ҎyΩfY--g]Y[<+O'Iyf ;=V]{ٟXNy,Sy!Zy!|l3a o=,:S(N%P R*]b[#;U@{ u C޻!!RXsDVrwp1 J}F@}KMH\{TҼ*Bb@ĸXPy[@nD __Lbye&Ͷ/5nH_е8Zm d/ X>e\5E 9H .GG-^nx|S><ב<5SPҡTтe>=8n=n'ޤ>C3 =BDq. ߽N*o[ww{]r}$|q _|+~{U)"fwx.ew!܋/v>TrY"c]f#u! Du:WpsVWe vejP.8^.KUgdE%!mu14=fP)!d'YW-;ݟj1`UK9LcO$Y3VDָ?d Hm0 }h6 XͨƐRNB<Ecܬsfj%6 9MqV[ Ƅ7J~iBia9wФ?R[ W,;j2~Y]NnPlJF9FxUAG7w.Sc{(1 #μ_ l3cJ .9e?܆#Vei1su"bNGP ` aCn/:1Q 4'(qpWhĔao}+x\G@D'k5]߫VK$¤mjɵ_h ,mk#АGO`Q+J'ВrH`Ҝ6 C᳓ܗoV7c}<$r]gG-.=T}_9[.s{XzUle5 dC=͵`ͣ'.jBr{m ދ'Ӫh'{4T5]]22E6R/hw b0Q۟k_in.`!?"Mottjb0i zgQm`S-eĨg_cMfmz| /`xp%{^@^J)ݝZyʎV^^8+d[WHF$  vPN8pi^GhN y3>ձ1J{K2U% 71O*lY>9FSX& /C7\9$i<7}wm[/ _(y b tڧɋVukI2p '-qK#C:p%Bl }" տ쀅=@^, df/oZ?,F{r:$tTʒQ}NE_oGzNx&?>Z=cjq!bh, bGHbs7HnhȮ?T9EKXP\d6dtUljYYK.ʢ@W|n5o Y20BauL| p^Fk Fr#vzĜ.ԵoQ y)"Dt#𗚫Ѷc hp୑5S >v,'KS ګ|Y%KAhYb¹1qyILt)Y8%1Lp'v?,5r*{H ;00(L er}ql@A,JL%@ V z靬0L'>et"BzQn9U2:.^,,{ mտBr~sΧ,ְM&N^|3BOqYFAK"Ǝ 687 AjlZioZ!QӳLp߉1IJEr>Y$ࡼB%~T=wtp|}҈|hy*'C6Px:~1kd HaVv`[w-"_ztCRjWэB; ħN[(mW`TppAՕ_Ֆjo;xJ` v\p;Jm!03y\3[D}wwfP`nG ʦ <[kj:` r t?=^nyzdl'H{>QVzEQq(ofC3 ʱ..<29'QԿ5ܨN ʞ/a KnbbKRgFEvH0bufB4#X<۩;WֻԶt ?^51?axka3崍) Alz'p0xOTȐҰRQ~qJr*[nԋ)iQbTysDPAi~wys~: !ȮƱ`P뤽2R}7#l]LSJVHC WuMzo$)U՟o^(4~ТqLyWdt,4TŅ Y37+%ߎN'Qm8ܽ}RN~T 6 %#Rcp+VFY(E$3{W5D[ *W:1%ڰg9#6}uu_ ʱ NjdM}wyY z)ֱ<Χg  \-(_ əY}28iml\4#F2Ⱥ tviنJ(Hlftj|΢foq{9HZgٗ6ġJ^ oZx˘΁iq[?R<$-K7p峈(^n4`pb6w9JiLwŪ` r4ɒ:_{Cnr/> Q5tz.bvTi7d xEݓ${rO3-pS<ˍ;׵Chh*:z)ӷi3$J#lDU%;tDɲ0|}4}ȇj d_GGT.?c؉W/e/P4f 0+5L(]5jZn(fKrHW GSvdjd@"/ӝC5?!S`UP<7/ofuhbUG&3ȳb x1^>ʕy_Reut\41b03nZPT=>Fi w+pNewa{y|W cԧJLھ$"]:c'*vnE @B(ȲtcAQFrHBoZ31FipX%xArsraݿ׽Z] 7oXԈK޾6PCk?iEzwB4&ȅP-..6MVh$%z+m8Ft 6gG.ڗ\ Fi+Ǥ)#RijV-ixo֞1Г9Zx_[(*)KjXly,"aj@^件r0; 1`N'#YX<}$Y^[^%(%oL0h|QM]SՓuS9L)! zs h݅ X62nc͐?j#RՙuMȅ8>bZҶɝ₼#;6iWpȨ<1L p2:r|NyBL5^jŻ]A+Z?xl!Spt:WTQ&STU3 B{R:ae7z}fۿdC>綳<<64#SУelgG*R6NfgՏ#d$p۬! s ONr AWE?h;dA0'7 \)m$>2h;, bVG>ymwe91ć4q6|) c=,LT,8)|D +9shܫzp*O٘DZ}>qݩ"82E̹.A4':N? sADGq a }sWNw({OvcD5Q+!1$dYPfNVZ,NqdH(م0䝯Y)2MsݐF<~cS1ױoۧ ,x:LPqbK U)nj$K 'xWDTQ02X3Bw6fmTyC؁LVÄXIˆ}#c^!(KHu&_",RNA(=/U΢+jf1YP]ANICov!w13]s=5hX6F~YJe) z~A+#|D9TT1[# ԷVdﱵ&J#] |;A]NiojRx]@~^AjƻP~YX-jf8Jb\5_Xݥ} ؆BRoQ;jϽrj~NG%m a7gYdY'zHmi%Cҳf+*.:ަl22Ш!n%"n-o}>(=--"t5c g Tn<<,==  \ qLjl|֋>; ҇4aLS&`ץc~' Y \WR_zLs#fY//3LzV"u[2X*T̟J?BK\\߮wĻ$LH{ݕ71mre]^N\87U1<DafCEhSQbc܆-%X"7h~${󖺒%ND7s*. 87EtA;:zDj[H=CG"ۍĺcOjJwXvUm>!H'&AIv;#"'Js\ňeGaہ0&Zeգ8ȷDe$$">|R  L,-AH^5>^3?"hH[$Tkq{ݮsF3.s/Ḃ}DAE9Y\Kc$MF?%# ї4[B/  B[V.kHZ|i9{:qʻag8~iKȱ N/QX{0eVUB%ἼcY!bYl~ ^, PyhǐKt(TrO_ctC?['CfxRd@,q٤OŲA66cg3R TH˘K_im=i>qzx9?\BȬq!-n OARml6.K]VRȠq Q㤷]{RlQzek0Yꃬ<3t<PvvF_+n\,8'+fL27w$g H↨!A9Pa4>]>Ԭ-b9Ź9 7U"XF*į -oIYΝQQ uK! N$0_ 9ea9Ÿte'MP]>X!3 2>Z qT9a^Ti3 |U! ~y#A,]N9`[Ov#8HE]PbUt$XL$.0T`NŃO^V-mtGqآRlhxGEALڻ!xdޛ2|mQf7؜cXHI$ʴHj`MQlϋS>SJ_ӛ;nĿT;'5X_9bMu^ؒ1fI*z(]_6HCQ9Wus%AG/ܕ#9B=uG)3qD>3 l )+ҲQwD;$ɐY 2P|b 05岊hUˋD> f狑,+u^URT"VzEcA^dOb\(BnZX-^gx,#73xvNn6@J_8mTe9#~-94rbkK.olJ7qxsQ%sC-Ih /5:k)xҰ8#RЋj<Ȗ@ } B8Ģs`e5_YenI <*mtD8o K0\\Z.|.dC@'EF"rQИ{w¬'MX6AH69#yNjp }P2Pɪm>&[OVpnTU.nvQӡWlG.(ۈjb{Cx NS\r\y otGi#,='3oCN_0umxYVK`U jHtO@ʵ-h1@u endstream endobj 166 0 obj << /Length1 2906 /Length2 30034 /Length3 0 /Length 31698 /Filter /FlateDecode >> stream xڴeT\ͺ5 qww844www'Aλ|t֬V-ƀXQ^d 9330d@v f&ze# \hl 3r8- &Tp7"9@ht;M9=@eP999@;sK; 58Edhin+=JE2F& 7'kK)@A r-T ;1 25j**Ie5Ejpa{{pUQU ˫tI5?Uv`tyUwpt9qUaU-Eqfk0\N7 03jT3G? TFncFv.Ni69Y:9;"`fiYcWQ?ѿ ɂG `( |b`Ag@nv^nfigj[yS{F5;KDMl@gX0nϴ636ěl>f@+`ji taA/3g9Sj AW/ y#[ KqF6_\AF6tt*Z:XF3d[ @_>DX`/Xlb "gd 3ZڙX9FFL9`agx1GϠ@ {398"'Q0AF#N& `XR+Qb0A`. 0?Es\\ pw?]wW pw#npM?߈jdb lSՍ$)A`toο5l6[ۿx1IYl~͟0?.F6*`TGYUw[p&`Kwx9 { _`_L/" X? ckE?gv.ƿQbC\W33xF?` @rcc66.-lqS |%=3xu*F@׿g;Yi dcdW 0?Dkrp5;Jp38AnCpQ _,J@1Ǯ﷎.T?y8;WѿB'] |2M?{ѳgtrGɿ^o% h82 -W< K@S&f1} G,g(XAQM +$ƴyYkN1U6CΠ!_NB}(U6ђBPq$+eU*nYJ.[ s{Gt4x !_6^6 !ƾMSQWZ!™um6/|UBYgx%:QYNy6i ЌC^eD* KJ ?UQE! ŸZҕv[YN`GUܑbǖ>ԭݑjM 7JߙsY'G44ll޿zUӌ|VCDS)w Nѷx[8 Vdu\6yl,KxbY2C;E9?&r5U3MyM+MӜUzE4ǩ _j!DQ̩dD.S.<3[qp^^_ԃI=Andm%8l +4_B\x 'vY݊Ա oMMEE[2c8E攨gFa4:.k }Y O"P:/p[v&6Pq;Q64#]WrLlS+!3> 3QD.k,"quJbbuy'}+o'vNͣ,=JMh*fg0UK7*~# N?S]mZoG)MC;FR(4x ]v#%.>ZHoK7P+ͣ}RWzZ&>=@3y[Ԍ|ۢooUEuV=syY&#)g4TMHX&bшj0eƦXu$S~J7&Jk+ɝB[e4]l/g_?t 9ͮkW{r3 n5=ph=`ݹEsk%Q;" _.{9GD`-Ïonߩ09s$N,4LŲr+;εaPMZ#sͽͩgXFGthM:6j/$/r)|8_HvA+Kéȵ ybLC)3x|MD82?MiHxgFL1v>棝Q2!Iw.܁ŧy0}oѓcȥ93c&7\䊔*_VZ̳IzeBg#-(Èwlt0~- S\ B4P>kFG1gT_HKXbAiߦtD)>Tq< I[#1ԇf6C"d0T.ʞw SS[bPy<4)= ύW뽱3)D!6c|c;V$/46:̊Tw2&jZ8[3>miV׻"Q3NyopFWϦ7/Wۛ Wdڴ==y,mId)V\aH~y 7Xt-ÖIA-1q~Z$-nܐȢAقsUZCZpP,[tg°Z`KYF]>5g)91ϧ*3i[ds(2K#HA^O aڮ~zb"] is@OoRf JӠY:9XC*7&8&W\6 KHycn'2KSv)&UߍJݽKߚ_cniޮ[- 3VBSG3zXɋϸs[UA(ca6_D/ E >o+c!Q om.X~\~&*/OBa{8"7B~Mmj+#Ld:_4GɽN`Tj/i[$SV,iF($Iw럓*R[]aRQe`ֱCz;znЭH x.w%#MS>4{ٯqu]܈705ZEJmG8iF9G ŭI~uU+QőIh*U7iYk# #-޷P=Bw.ԢGșQvj${ݶO-D+/!wUS[!;4pkX!: J [Ɛv368ҡC&&jA@}uL}O8dzۭT/yyoՕ4#aZi6- y05"-i۫ש1ֳMԆ10/LCZ,-!(h (Hx#E 7}v BᨃF\Bx7n̦V, ӧ8k,س~/^1>aEnSU^om m1IDj@R#y2ސF1_lP=W?. )D[`/uS_-X:8mc欕fڱ^E f!p+׀kS"c 3o2tI^#^˸ã}(nB`\^J<|ҨZkc__[uK~6'GƓ[裏tԔf' L_5K;lf&*6o;UFR_B ʶ6ZSE|U+/|~aQ< ƒ)9M`=h&ty[${Ȭk O ѢB\vath?AiKr蟻^H;i?NG B*@oƤ\QC~lf9;3{K :'/{I ,'*#pxb[F td*چ!iz8kki~'whd/ 5Z4Ui!9|kBHIЈ)ǮQDa.w5V}Y<8:#3!${:^Ŷz밮TMђdJC DJR%df=^ۈ%_L" !}7YeEj1K_OsZYzxjf% `9asu}6V{%tkP4yMygsJ1k,gi`ZM}>AP^M.8YquS]*}6AVX=kG|O/?6?0ϧVdKMwGE ~ي@wF l IPЮ?NPwLvPkóg ʹԦ׉,Ԫi}lȭ)&]t%_ajP<;+Fr ax>'97F@$.IOjuoôh(a IfSbCSOflYѶNY*:M+quVEfk >֡1;SP:"qŚX|Ue2)蛟00$?1e-,-6d-.$ 5:A)*챪tEv}OA^o)Vrkszڂj_HcxLqcWBVcA[9_\|sd"|:N%eg AӨĊ6/a$e0m|p_UˆZ afPEuHA?% Kś*VGиIyᆪ72ʲdl,kL o%zQ}&P UGqŇ5N/cĽT;'[N#>JJAM|MOvUB\z] 7iN"Ӂ'=99WQMFAQ|䔦WhN/1Q}p7fA֕"++A+ m۽d4֌X? VD7=)N2h߾7\OWi ,}N5kJWy5.Yq1^ oL)$Z;hRm:MʢP{i}U|i?CKhRfg*a&*G5i( (M\BNT NmQbϓڠM7v‚ `CSԂ劼P@mđHE59*ߋ&5L-}$O&ɤ,gYV Pd”Մ7@ť''8꽮- qwj{SjpUx<6np~kqT[HMufU+;gi|Qh X6+ak^@,_c ~2?Ď!pG2rûd4[RP CEIֿI~ _Gp NK'N(Y^(rߏEÝHj6y2Pf!>9.\M" :dv'0~hg4rVC״wKw'{<$ cBeFc)GWKƀs`mcW :hYAzlfysWe=,k řgPx{i9)rx?GTa̲o-\"h'?jku g̑Z0L]ĚC4nȟPpm%ܽ0H&' oNO^}}DڪfjJnVKQ/un nGoiI^lS#h7NZ|I&Y ͙aԭ.Wnim^FCeg(ȍ,X霼u,s*t|dwp|G*0~*J4\ƺ˕Vve#-U0V,+BYYբl ZbM+Ouݯfm#P[akEղ7O;CۀMN'CfHUA*H@Gj|*rn4|ZEf cmؑnN- zA)B%˺ReҞ tTg/hT f7M؏л)- PuIs4&IZqҷ&oUK ^\-Mp6z1XY1Utvױ j3kCpB|;[ۉJ3ejQ< :7uWZF蚜VŒrˇj6Z{@~% Ce龎Y8TH&2-(}tDw)@˗9Kc񲨛 '0"Pp)?51h߷ hM GdS昬8k;DgBת~]Wc/ԛ)(z(2,J=2V/cw9L~ꮝGs%PE>h\ؖ-Xkids_ ]B_7O J.%bEC * YW nfOfENgg.f4vJX ud@ෘ:HU5f.фC*[_g H5_þw5Tvt&DӍ\~Od[ ʤ5oB5׉B32L1qAA-hcT9Osl5eیJCypwoYsU?# q̘8ioIZVm7JcekaefE_j.vQ$Q4 $ʶG|@nqWt km@\UQ@0-_ʻ_fI"S~ct: KOJM xMu%\e\7.ؤk6Fhɦ(Ĉ5)u[Dhđo&*坎2;ōǭ jiIڣЉžXIbr TU~swX>I4:7qLg qYhSF<`HʛDZWkl䷀LP'g.2o6+9{ZSB6~<~zWʝN!B1.-׉[n*Ȑxo6("Fo/%r_{Qſ?6Ѝ/RID09Rdٰcˢ!836C$Z2RI^ቻAvzGqzd<rJ./+C $'AisyyEG#S"Q +s ߴeןWPnIj]9*A4$f~R(;0`PQin"X"p-00yUDʙ5bn˗EUE!/W mžW꧌~%=YϊNƇuT AB(?H7O!G~L Լg(,fd2cDkðE,SKJ- ּ雫eُI@1ܐ2(13[qwN~87u߻˹iH|T:[O,$Kn^ yoqʺ sp閧7eEhG ~+zw9hIGE&nH{T+Rl)r\@_>U&35|s̎F=?Kero ?섰A*zQ򺕾q76qMNy yM"Q>0+1^&+ݔORN5*=mRB|Xz5GVnAsCm5B(7(Ir6F*hcJCΖE 5:]RL 7"};'2hޏWۘ}+rDmRt/w 1?$_ezP(D!bFu0_CT|J\UY5-Wuٕȉg?OA*$FlۑtTgYE\1~2Ys(4Ws-z+S, "0(ė==CE%hM[bU&c@.1--ɒګйƨ~3>L.SUb/sд+u]=N[7e̲?8tEӑW[m$,ZYS,)YC(_+0uSó&*G>$: %עHuiDٽ).Ƽ~ͻ4/4&, `EC[z}kz+cg[{&9%$:o3tZAJ?iM%ҜG2ӬLt`̦쐱+_FLV W'M&:`M (oIx; ӄN%fBvpU|9?>?u=tPy+k8̤l&Vd Ua}"|e͙OH/ rћT4fy$Assa#zJMYBTcP=x$c8ǟKH>^v-]fr1e25]E{-WLxFS'Ɲ%~^ם+C)8vX k&>I/:W:;i$@_idST'`Rܻ4ÓCY@*sr~]v6]^|GxT#)£خ#; tߩv,m}9%B-*+dzQ:4xEC #p(_|H%]~YHeز л@3CVѳ}2.˰a֠tzfO3#W:.NZݨM fʧLNŹ-u_ZB=^dܝUVv+w1ȃ*o*f& دSlV: sx}%=7_3֞+GhC=ync3'dS9w|VqPS3-bԍu G2eXNC Dc BPAI *P"߱㵬(\N8m pjJIBrtb/ $_׶"8=BEA:o$(RhS _!MZZbumN&-E/PBEpȤ2\o=SF\6Nz. !J*׭^ipybxF9/vl1>4B Z.zƯ~T .< 2\Ϩr7ZRcG:7 XN:},I̗DؤhTǠx#:Aofh$p!$mSmJXX?f-*[ Ͼdg?ʷrZ1yV $;,P5Ltp9SJDBKchݦqjM## ybR8"ؐb7ϑNA-K n>M$B GqUaX^򎟷ٿ^4TC"A7U^w-} P|^պNlNʯ'/B8~]_Z֍qvK =O?jc-v`ۦ+M,?v8|IB. q\i Ϋ l?4AtS\<b@o;A9Ŵq ^ݾ7fݚ&<,*׬[9ˮZ'(p9m:?2Ge6z ` X6Uv{>djX[+Ɛ$#, +ށ&c҂ԇx2Z 1Г ?cվO'lX>1X9C&.D>[O{BwtޡE\7&x+ueV=T b2툹úc<!P@ ˑ>#=sU3&{,3 N̶`2[,#{-fZU(%|X@ܢJ:he!(G!~fye #dTJVTA`K~2^Cr2$| =0bCFЧ|CuES/yԃ_ _%k&%l?аyWؤ/UQNڤ?ra'l&M~ڏK<\Vt؉קvӈK{ KN"䯜x :Ea ȷP 8aKQ:\RXSg'O5#p'2FK2V/%ݲM?K(5ْHf1Ǥ5\C"o/9 igs;H9Fy)?uliJ:OX 6]B@) bPE2gE+I+ufC9Q9ya c.ޠd'^njQRFV+R )n)Xk6~.БeɾP;?v#;}T^;PҒU~ZkvuQRpbQj\섥Y\fUD3#34ޒO*Y n{1ɹ"oH ]5?,~%5ٚLϕ<"[ һě~ S67Fұ yO~ 7QM(ρlw~@-r~}h_[KvL]}yzUC S ) fo[][*Ftuz:pAаѧtq[ D ʊ~@ꨛhf$$X8.ډۤG[\aû q$;[wo;QN6/k2:!:쮇[B)$uLɰ8(ҒS)'Nδ{^\67rЗ}*Xy5TSJȯE\ FFQF64rZȐD_H'<Ͽf탚Qwc#M.*$տ8mHªOT$^Zq<ƶ)spJ)7(rO.xır+|-m H8as=;e>҈.[,cGGG2Ԓ${gĂȆGvIXaܭF[pIt%{H͡L A&o<ǧ0a(Z6Z98qRf8xZ<ɮI`Å"yoˎC59I;eHbo6b7CVrl;D'W&y $1$ k0 U5,v!E} 82慶3DaCJ׉J5=^Wh6}z)R`@B8c JCmH,$?|G%[ߤFt:bo+ ԗ۴[a>5yى!'Z"m}ݳ lo}WkMtJkGϳdĺ]p{C#%TF[E.g69IVvK<Kekv̀㹟R_!G;"\ˍlޠ QXJ}b;5вt|3?6:]n407eB1gf&_i aAE;(aD;1EG~Y8CFO¬#F7u(])i}MP8DM ~Q>ٛMJ;9bgX XTJ6N 71 VK0Rkp# >Fv(!6ݷ} U>gcڀ7!Ii:a##t׭8ʹe|'v㈤knHXk}aHyX1?[in1CfChegQZ17hA02 s@{Yݨ{'J'>%740[z~ uyjt&`}!: 56N:,>wI6u90PmV&DL)lzuh$5Jz9H!"T}UEUJ,tUmIT&1 3 |s8s៖3y/[HZ 'nWCC8cdΆ9hC9+~qYLa&ga#2 pB~$V1M+c쿾P^Ys sEQZ"}TnS:(DyWH"4+/~,)](E?C]" QҌcVNAmowd]V9 ^wrGٓz-kQ>^^ ,11叡DdL0Ũ/Gbm^C? 5Q/l[զм\4(>(qlg.P# Zg8̞g⦈pTQam>&ҲGfiq0(YWu8\KD\fTkcg_! ̛N&gxWF ori6cC[(>8F+%k3c.^Dz!H%C"w*JZ:aMxbU3tV |rca!ԉR~{O\#d62PHVR )T8^f"XRNaL+!iR(fi\/, d_ ?EehJUܛ"kj:O C#o1>{7ֿ_/۷+2q9`~ L@ot5 E&qD_S" k>m@깛۟zGp]w]5EЍkxǕry0mGvOku4h> ň=';m7<.SS{P`+4· y2_, k0|_J1% 7P-BC^Re]]k t=xU ]^;[&l1U::en&$ezE3EJ 93ƳN0*Ԇ X5N3q|O_Y& l||P"DeЪR$\fe]9Y7;)Q~,A!"9_dlPt1+V|\c\#GyFblJȖ]F)?5?1#SIT7I22T.bF|~ MG'k61 X^K2yXf0|~%h&?g;鹫a9>N 9x+߬-%M5;*p\B ,BvuJ4 Gցgr E#GZP5a5N3wKr;æ&Rȥ!qи=ɜ2"jlIc8:`],jF/ $}%aO5 v* =F6XZ z3&ljP'z`EHK6)HCJ ?#ϗۋ'1lnyLEf૚$dFњvF׋|' :X+pA1=zw㊼)aQ Tʧ$)"wն> /:sI{|Ъ س\&1JJH:9yW]#A7weRĽCc9-.Yc{ի>c~ܶ*b`J5TVhbt!,=׍{tF-mMa}4{f./ЇZz?i}C\|/?kC߲kz[66LgiyW@7l3J$ IgOz U_-8p#U2  `F^>D2awƷH6t?6؏NKHqş! Lӌiu=K Z>y0]+n邏hZgm|qkqOH)VC +vTFh:dqZvKyKlmw`O築aM7s&9tCkUJDgL+Q@2 wcy눺r7?R @ ME3ͮV B5y~d|Jp}u$CvF5YvZ>c)gN6|b &)-݄37[Ul XmQ#}q di0%hm+OX% JTq 81/UX x$8lt˚F Ŵ7?s LfbHK*ؚnŖZK s)wk(5)Ҥ -2<15& NKT,;!(7Ⱅ`)oks-E1/Z5@ qxKtQT?GQԌB*kzDeT]Ĥp"1繡dM{tMwT=݈B^v1s cJ7VQneq\s ܒcgO [K/I9(!k?`T;֍.o_fS6k%( K܀fpThqn%95ʱe6k4N갢(g" {.7(mO5aӏJtJ[הg$odgVH{ { fd@ُ܃F+ 'ƨ'rQjN>i % +W-D_E/I-h54im]SvqO% r]6Ne4 H*՛IV7|ňFL$T fH fZp_>Yd2BlYӫٴD62|׌E?m.hЉDŅ^l{i8G mʃ뗥_#xq@J*Dc Κ;§xWhX9kE*TMmp'PDqқV7c`&;OB;#b5]žW3mڇ2@\9eNkyF = l;]tt"~ײ;CQ-ik JrLr< $Xj;u;&uZEDG4~#Ed}P^ =_9Z!>%KuFɄ=U~ad8BYgx=R®w b{:|%pdb0<+RØ7z?VrX$u㘜;jk䏬]'6q'YI֜qxw`/Nu[6ms[ua,$vI58TJu &SgQ~~}NP!W.{,KOc fE&+;>3}i]7m wYRЁCwxkCy9p.Z{9A)iȩB!bK({?m zf? tߪGgYoN:56菏}>&Mg簳a~zbgFfdM:*:\n6oG;L()3.Zd#C:k=Or:H#E7Y㤕2/‘v, ')Dh4 }Q<0Z;)ܬZ#;n/VQܿ˱ގTpM!#3STc-VܬoγD[q)BBr5("`m]Ӭ! P}[ YObkMuxNT c83szwINLGȃ^e$"2=Hr2˻BulX[\Pקu.W?^UJmv]i BeB#+ۡdFF|$|²Eb*KV0 #|PGO]#y =&цa-Wc=ο$ُT]Ef^H|io{JӰRDe&X3Wj}Hn '&dƘzb+Fܴ/x6<~:>4YS9ZU|4~l+լM$A÷ /,zi6f\~Ļv๽zim1_o{Bc҆{MZz~ sCp&0=SX Pc(4~'1x}s{2_%O ZO8 xIA5_g}#U( Rm(_βamRlS.?gt"IuGH+ Gjk-XJ7!Ɵ Nwh֋' oZ}WJ=h8 mDk?; *X9L.r9R"#6OCvi~(-+Y& nR #L}VA5=)3ȸ |+$\xiתjW"YYT%lavdGEklOAL2L}ՖƤ@i%,z ,z(٩SLzSMqeMq)P*5ĮWl^#PEhA!&FX-ʅWԩH~g6TEt`s:c.`=<˜ĥ!wm%GD'CR͔%B(޶~<*m[>bYGUOߨt![xZVM.ʒ'Ƶ q ;9EV]oQE So>eChė[HM]&q\{8IvS #ɨ.0-XW0K" ւI[?[t;nf.l_Hgy4*PK TNuHa4/jU&LH']dѵymz;AiQ6d1^~ 7"[>; d= QD5p eqM,/5M=lv37@pܳQ!yGɶTcEGY%.^I .jǥ먅/QCŀ|UQ&9|8)u#EWA('y%ȁB.?xvb;z2'%/PYfZ8A{Ock zH $ r^5 ~`rS^U0w +TAKCJGГD OV?r"ۿnk?42N9w2<ޖ.0"`*oZ"ݍYcsR`}n!8Mpj[ӮRn>tjB\bL8u i6Ț x=-sv6Xn5xJ>Z: M [Ib71DHa{y!3C6='ˎ&?e5}PIA M I_tw8kfjqăڱh vb. tDhkGNJ^# C:M 6Nx:P2S0>cr(R~W տ&a.'D ~bgӳV{x~ ±(8߂~m%SnCg*q>)̪C4$Υ[a %ĢP8Øe)IzUm< 2=diE|\s$P[x/5d:RQ ?Q6EAǔX"$` sd+Bv4Z)i=8GAv<8z][ZޚR%^r )v6` B/5n1!O45^2^\^Rji`x_q]Xm~ɆibHʵifsլ dhHz#w:mi J! dp¶(U>҅_S7>D$kr:CZصi [m )x<3,|tx|dA9U>6d5yb3Lj\j ςNa'G dNL$6߶$HlpaQvg{wI9~WK'&O LajsNjޮOz g*cJR`*J@lV(&ozP>V6jPj5Zd^E(xN9pI_ Bk$?D iWz"Z# cP“p.V?lnze؞r|z7#;_˜'s[_2&[]wL ^*5U|m#DN`Z?eQq'+Hmw+YkU39V@6}SABPgC9N.ߝg^ Ф6^jC1-2&ȓo3rBՂY]/H-lXG&oօtpm"ᗺt2Htb.T74I^=2>5F4"~_/o4soHf#7՟wHF7&tE"luah_&( #/zVl@!_@Y&ǯC[1[Tǰ7: h {7{λe|EF?ӳ k({BEL}?4ڑkKIGM}խ מq'{>Qlm=؇M㞽lz'b+3o -Zׁz'scY8;eii)J=?t w\G,V$m]t"j_P|L]9R-o*NsWHĚn,w*,vQQ8M^֖Ot'oK_r@2z1h21IÆ W 8K!|t!Htбwיj[i;kx"&rG,iag:60IO: <`f>a"!#Qr5iȌ?SϟZ?YTQ%YJĤșbܤ!Ic=Mx`ч(mGit2.;):rpSsL Q&dUR@آI@Jhyg¿4iR oD۸9qs4{Y" B~wq҇o%FO߇oVh:"ϰHSN _-Nk彧igHMWM |nK6$8bInMQjC{[Yꍩ53;]J:o0c*ş]O/u.Fᄒ3iRCήfG2kc7 ja6]Jܖf~( WYx>oQ1@|n$,I&{E,?<+_Н Fm֢ga*&wmkQ>qEEy |>atw>RRO'v '$7k `ٸG\֛ N!wƚT QRہ+_hQWygȺf91ϱ[j4W4 W2_ڙ%[#Y2|.<|cqn7A](0;8\i x  Jf D~z0-&-wc#caEl TGAD.iUak`t!zK,ӗhh4nq`A˫@ݥm3 (ꤣ?UOX Ķ.WA^TŜ)D K[yGݘmƾ5 A ?+|4j@f{?5CS?@Ҝr8m-^(G:Q98Aўk>wGfbM LTOF 8թ6eS8`dE.G_lMz  Hqd֚.Ǹx&iQìWy}қm7wk _c!aTLكo(*d'?χP6ѹ^6@>ȌgBSUBԬ94bd~?6i'm4J/ ~DGnz ^-Jgn(\JOi0sW1pƪ [BO"AuF57/)`,bLQK>ܚA:ԉ[#*jWɃ>ٛf[ȁj4*ej\:7lo#q &1K Ky+CYaҥm_ڔ2 -oN8ظ.1B%Ҏ%糴uADN|Zʩ[w!ӐwZ43~6 (d%f /za\ԾkЫrg&9ępnS γ*Dx7qM\囁l!C 'kcXV-) t#|䌨ό譗Di'-SH'WۆԘڱv|h!4US@_ASLFF_"_5hFyio/LUEo兂&5ᚘLͩ`w&/)sU4WE˵&nmmVR Z*' 0ܤ2*Kiɑ(|JjGY jfq̇\%ok+wC|i- h!0wTU-o0[U{ 0%;Ҍʄ@HB)ۂQ?wő2NLN PqRW`yNbr_lҟQEæW٘⠢%2ϯdHv8bf3e=z[xerIzhY}p=m$]qJ0]Ʀy@"<2/=8e/2k,Gi!F9Qj=TNϣdo&M9/l8)0r"Lt\oWͲe:}h<8|ՔovЀbYj U#kv|7[(I(BW.za}-FSxwf:?.;/c'X__Ls(b5c& `1VbiIL3zv׊mIN] Ygwd#:MvP`o(K=\\jy{J" Pw 'OІ~&QQeTB9-vBaNx6z5*k$S5xyC z-V@=^si<;eA$a*Q:)š*ل&h"\o5O~I81.SKk9Kh`yʺep6iVC=Isk ]X~b~7Idmw9[2R cl1"WCZ Yss,O tsA}r]M! aWH V sCcg6TD/~ٹ[S' Z䃥츭zx(9t5κK_87`4vEL{ [ʭ8W~Oagϰziw]i|tx/s$SKf=_!NaZ.ߕ.5 j^q=n wYGFՑkZ/ n &rT}^w?y2 k&tgQCuyw`d=ORf'9~HFUWCWy<Ҳ"rDӃ2JRaD(ח?5`߷NŞvǭpnRGxʞ:槍+v0K?p^[P 62n֣ xܴ;w$H ҿ_<a6lΞwՅ[xȜ\XX*Mwx/N^a`NTTVӞ'J}[tL~4p{Ac; q>Me*:gpSM7J^[[ WJ)F+Gi4UW`j8Qsӱ1A1^/b38a֞f#ݥO Ѭ2Վ(zI jh[Jh} hY=im8%\eWTZ&dɖ?P8P-pre 8^#`!,ٞ.-0u˕:ֹ # Rns{{5{J"6e:cd`]R",i>Pm.n+nA6JЎADi„_U,ȇcu&w F}x]6;J7Z\Q25J{lCQT`2FFܮs= mT[o&y~LԵߠLd>Fj"ki_bvI(KPdc2PqoFO@,4pXB$mj/>siO!Fކ F{9nF6$WtJr4!?^w87ڴpvj/5=IK~ '`U.ڒS!va,5B;Ӡ :6v|IA@lVڍ722 ׻݄5C:[1sSũ~ Z~aMz4Kz S;rfXyW۸&a vj>ʜPJ\` V*2&4U2r*X~kx|zmTY<ԝoFJ2j36Ȋ䗛%R4BEmkfuL'|-j\Au[ZcT~H߁)/}0v_ca-Êd,F }7X@\1u9kda J[fYD+45@@tg?4kǝd(ਐ|l@ZyۚI/eQ(K^ rxPP #egf`Cs²iV]hD ,|J{9Ȥ񆧰μV 'xhlp] "<ku+i.KX9V YX"`&xFYWl*~KQXnWu m*Ԕ.GsA?t/7a{d ]`l8?gyUޤ#ۦi[db,󍃥OX=-0t+ n;?~/X;gnJ'0Jo$~܀y+)_+_LkϳzGz-\E=>dJ2j@3zgO(C\1-1x8)C{l -GBR$=mV6$ S`xw|( Gp%0o/QϜݫu, {$Ng(`\2ھ410en_PjCO\GL"6Q`D ڄZIݖaKH $( endstream endobj 168 0 obj << /Length1 2497 /Length2 18920 /Length3 0 /Length 20374 /Filter /FlateDecode >> stream xڴeT mqw.ݵw-ťP]s{{yt;#j"f@I #+ @^Adbeaq121pSR9M\@&.@>%@`caᅧHN`'@bdИA.Lj="rttwޢLYS ˤPV=2jj)U% e5Z&p`5WqSSאb(K ) 5߯@{0 :X;𷻂+7FfC j+Łݝم d`?uK+g;~wj.^)IJX_bFi94&++L]&`CWg_2hF7A @w?EAm}Mb&^m wrvq;"`ne Y%SQPSg={&"7 R {313[rdX؃_bs+{3}7su`ְrtʈ1XGft%do1o1  3~v6q\\T/gYU.co-3mJ ޣf {[OYvٿrI*iYz,(Llr)[ZĿgY9XعhQ?VO/_:T\^6x~0K{n7Y[8&NN&,a`x ״A.`/{E8"E#n`xDY̒+Yb0KAf?,q p"p?ST1 pE8/>?\߇?3ق?;?,`f.7rt5?)E[K,-e` Xf.\? kkn񏄬@(CQ+@?jpHqn 4w#e?`@'+? ? N`qX:I? s69 %@p $@p@p^*G:ݮʿon?{ee'+= d? (\yx3ra=\jב>~ h 2No (R EtZ--n%KxP$S@6U H^/5оT2e=fLExOYBd"_I#([a9H6Hc.#1q,6vF_vIDS@Xےn7^F4 zy  RixOqs2eJ+&$dw~Iů_zRuM=ȎM'FJtJ/ρxD%qeN{2qNE:*`d:AVRlr"n泅4:?st/@;>e~Z4y{h5 ػ*AyLee/FlXRdPnjk)% KnrQ^"/om3}5TZ^PجYiT-@RK]mU]^2D7R%ŭ[_c^[nzaAQ^X#gd3DT3Hvհ$/v 7Rڻ0-e]0M90/#: w% v<`Ws T6c4:M< X1ȂP%`ws?_}>l%mmDAIQA/~ӝ8;a(l;q@WK4ekLX*:h-JOI}Tuu7$B\޼.x(UUUWfL<Lf?# Jۊh"H2$r/!ۚy@̐dl^˫F%22tSP$l왜0%W#,ȯ2u_7xygO$H~WCse%ҏf7Z=Zz5i62 RH0*BdWpr\|Ƿ)/P]v5_d.9REKHz(GemFrF|2Nw䩙}qS@_>{N9ڴJKb:WƇJxxyL\ \ݙ|ypw>eM \|Մ7ų}wH}y̐)svIleg赁4<0}$G3b6ImfXfLxӵS<ؓAC~ͽ0pl)L+ B+ʀ--܏E=<'hːOƑwX+ 4dǂzr/dyo 9YNhB- Y\}ԽCt!G*ZU0EROw{Msu'LJ&w[rh^EKn1O{ޅ,)xj}?= O},pHiey e ~t˸6P?lZӧXKW`ZDڮ{Ԑ^DwuizI\jqW*^<Ɲ+E*߻%惈o|#\0m9ĭōa 11'^n&|ȗ(_%Q20<7 ()~Bo}v6sz~+CX3~mrqC!c}!ʔR9%xL#['iGw1a9SmH7 #x(b7/y1EDGeqص8WkFǬmNV7N! 55*$ Cg R%.Y,)= Us,ɬE{OLUZulHFn~&=GYJ`W">'`C}EL̽O.eQ.Qr'?ˡ7c@\r̷ `"WZG-loDBNSZLڅ[nAjQR&R3m=fAhN8SSX<ze;&u=6&.{5(dw{/wJ⠶ WLwdG:%^]f> ^^dõUC:/IB[ )J:=|Hx_ ~q1ЇE\VG}x:Dm۾/> *`]`i*T9`n6)@ZqeFIlYb7vFv~Ƥ\A4gT@`5YW,<[OR.Q!\Ntkn'A̶HoIqKMVz/=~nmZD*&nMьD~hd8Zr )F~s8>l605l7~t0؞grQyxɒ_%({nހž !D#FbNi=yXo_s9N:9xW'ڂ ^G ~V2Uc@o".9ZYf] W%.D>)X}OVs8r;g#%%Q Mapjҡ#{ݼ?RtUtV-gy(ެW$/7K:E_΅Zt5WS# Q9Y`^ѨtrH, #{ӺKn/@5hjFp*]wR= m|R`T!yRzchq}﹑lM%;.Gd&a?F#580QK/#ާmsKh}F{m"$%ZoHp$a|yL!izi aOQ/u;1/o\:pbDȊxAUOe0$ӆ^Jv)O-S^8mW ]"]Ǻ |9ZnEohX jviJ& Ic)whMqZX5t4&k3U8$psw+ge#J1> ՘r'NL=ZTlj%c.wy?ݐsl=ȅW#:Ώ1ڭB/P Yf"YIP?.Gyri#OqTS A[ʎE`S sG]!`?n}xeTnJ"j_;9n!^yy/` 8]pg.ZQO~`-dNRT+TAK߫qUQOQcƾtjG(p"DO6K\ M8>RjM;̮:86;(v7_< Rr-Tg -뒜S]{KYKoS wz7 J-Q`VëŐ8Sy]/U?sJJ7wBc __^܋y@eԞ៍!.ӓ' @"y'9*'RE`NJ^ɺC 6l d8!ȺOҰU!OJ`we*͌CN^g]Q"yOxpjwI nTՑd=Oʺ0*Uɫi [d2샗;9x8)6[jx(s.d{IrĬQ|;/![ E:zM(MbIQ"2$=-g/A8Puq42g#s=<ɕOjgw: 4 dVK#S*Ccgj֨ɘot$?G~=q (O $;~'KAvF3m#H;tB;mG.y8#jP.u]ԕb^R^]:@1낳pϕW8m/)Bͅ¬pŽN;/ Xh>ށ鿕WU.gy9EOӌ!ju_o5* wa&g?;7QpDDo5xġAj8hŽY%vLYZ?5$[mﲼ0_pgd ҈傚ቺECqz.oYu=-Ś4M>ļ))f/Swոt4Ю$^nJWOO}(v?U:ű􃈌_Kiï?~6D55J1hxNÑkZoc)ˌr? W`T`!)Aj^epWN+|wi\Yužڍ5B)niX~oˋ1g{P&x0f ߞ&U]Ex*rW(Q݈ L۰vY\FA\PA`b4"ýڝɬP['퓫Mc>t*#^P7nMp}W<;<1E?`]#2s @*۰VL[;tITOs9k0띭|k.19):q\ !Dl@2pnJ(Ce@C ( ZF;* t!%Cys9"7PSH@.!: W붖7R-cƈ#ڢm9d=uAq6%tE綘'UsmvK,Zbc]l~U9Sy>KrzPE7 v)t E ")Fbn<T[ azhN:X֯w"XpN ۟ FOM%9u!G/4Ey"y˗#z.+Z'e3,D߼%~7d0mφcU)m;ˈm~5 JJv 9m020;c(=ʛRt߯Cdixrqw(WjҘe?BM4WA"VcK#tU\j'bLy9'ꝊHI(PLHn?m햺[$0# Pl@ A{#hTeF!% ې[5XW$ِE#O(USA߭OI=}}m3 GYdYn+󴍋' xu61=B &34wxl,13bL'ļ oM={Dluw.~%Wnf(ii%P Usa,Ӿ|R 5{OWdtGs(ECMۛwX#A'?}_*`5%m$emHׇb޾^m0@xWe\X~rԷ5k R ; fVE y{hl]uK_Sy~Kϗa*:ܳv G\>겧W`-u}o#wY!VuR U lzm~v} b/96M ?#ryݥV>" ߐϴL f Se`U_e%$ Ĉ>b d_b#88g$l1?D;듈^#;szSb<Y`W6 pM~ z$WߤǧfWQbvCJ'/>ёRCa۶BStd+3!#ϫKy^դbfe㏸%RKN񈘎OYlc]`mc氝U;g#W7ghBmLX^xprRx(cY5[xCb)1 fn> &۳};,_Iqq\xHIɨ~{hFJLLmmR9OGIlNLX-|.d~91IV)WKej(]"{Z{_iIQ[v_BG7ޣ68.6!vg3 K+46+R=a-e"Ph!f ]TeeJWCxlͦ7^nD̆%ۛL_):4΀,v:x^@1{Γj!(ߴXLbcfMs+k1=<ʖ&Ҁa$g}z nn6+5f1]2?h:¦zijzZE!5'fš=,>}%M' 8BL8fJWaQPfcK1m9b+ ɖR9S.m "1S`^(Kki;~zo:1ֺ d3XC@E2q@;}&[yà9vg/K1ԯKb.aJ#V1 \!+Put}1z$-h^Hfֽpd 9~e6~(DbWelx//dV^F&|8WR:ʥQjAmtxL*㙲r2E_,7f#;O rK_{e0y7ЩٶpC<ǗH_9(\9_{;?HdttD7Qpъ~*T`a~1(vq o;)% eTlH6m'G(H[Lx'M A[ zl R {]z/m<>tml:O@u(XOiqVח˒EkwsѰl$ DWof5"Zhox#KQA0˗ %:eZ#'ʵ=zrSMEUXBQa+]-黖~zuȚ0f,t`.sX #[H-vx÷F6·HFwN*ƖJHO.kf쪥G^^|tCh.$MU;U~bPLqӢ tF 5*Hpl?kmQdJ1:#CuQ֘#A %8Ke)a8UD[@SĹ do JK |TT ww,yRV_BTTu$%<q7:Q=ywSs75biⱎ )<&/LwKq;IiW6Ֆ%M_x}D #FT/˗ WT-V_tO?Um!WqiL43~| ~ מG"7VL٧=UP\r],e+!@2?xmsC[Ʒ9M瓳 *WJl*L<8|Q_y]fIBA{3F\̅5Œίo&/Kk"dc ڞM`Uu2K&Wb*r!QItS{`,}gXXrR^;v]lՀlZ6L͊YB#חh0‘Y Sd㬊}d.`{ $]6wA򇺬*o8Ii$m)NRlvx/m9Z`y>s>YB{sCXΑJnqEUD$eP(/U#iaAY!y}*7lޚxREEniWI|,EIAvgs4Ԓt?mb(HЊf]Q8^BJ-8fDl 9}V߶\I9Avm;EcOXI A\<: iWy=MyV:.`qXLmU 5}20_4X,ל[ˏg'+_̰FR֍EmuegM N tkTX8%I 1[& ,$@HV+9Ne6r2"Hk G^kAkL9x"&6t-sC`E4M*kNryҾԏwLkX{KV* rĭ~8ފ1UDג?X&'J}[*Tnc+ H 98nצ-Xkf?`PӐ?byE:?]h*Ӹgh h'VUȖ'FEΝ}kd|@gP;BXIgI]-o+sd5_gG )2xV3*oONBi +Wv+ V2t)">aAw n b406]?,G=K"=Tg~VB-+of;^+qG [D 2%A]ij:A7.o!M ,<'Fd)-BcK`fTa$Yo\ ?I>ãlnUS_psB ut?3H)ht>\ܫjBd]h D0lY>8A{b EYv}!pf:5-g sltf]Ni%cIX7NNI %H9oT&FE  (F]ʫ ,$*U1Rŧo6Dvɩu!M(k?}V6,y ںW~ۿ,%imSvҥqfjWeA=0,aBC[liB+1wfNHl H z+^Q_&ӷSϭ h%)T5MQ$ Y|?[* ScaQA:Pw>X݄O.ɊodzcsGD`l,5ez ݇,s(ߞKȻ*T܆]L@H0aR3嘭]A4'BY &VU>T I^/^;9.8'_)_#}EC%T}髥╝z <{fA&Se5 y"류K|kD^mg*=M(_aRJ*䍨6@2">бπV%*u*ڋGFiݸ;\JDeOe +dPR畦\ >Kᴴgp(48Qg.:zD*?ҋٲ/dN^ҎË*xѿ9t/!Ĭ)׈DY|$$;^y|\Abr73dd>*rF1`Mgޓ3mU%${P]CG.?"{_?e5?fDu䡰V"*it74F"P$.#o L(R4+ҮM0Ϝ dJL 젨4˚׽ü=$bdOI]~rH_(>1Qqvd\WW.c3H|ћԐ fިF|PeFQFd Ҙxל? +)@1oue6?(S{+vXD;pakaXQgARUP1@F }KR HrG &r%]50M 4:H&q{}eʹέEO`,h嘛rAԃ L·1G4/]~=1)jR& M_c՞~Mw2lh!|ax:;8X&p=_AF @Զ降ra"dy-9w)nZǀ"wӢ ,6zT^=Za4A *2.PH0:^v$ .Ƌ-s@cevڰ\Dn s ӌ-zBYZHv3tq8ȳłD_ ;~u¼:]g!$Vh> $X`Q%mͷ= J=Giv2ǖ6žM"nxs\7Ȳ<CDC&PSM Gr}SJW 2]BՉq=8lA$BwK-C\kA{Z!󆾩'{ Fh,6*zaU,JÀ%Y\Lm @L>3Nz cQ5?I?rWG1+ڎaӾuRNF TGùWNfw2 `_{ a@шX gǔI E \X./ubOtdh=Z{ͿTbկv΂' ) G+UT;?IOǬ+W*i'N)vpjg(ДW7[%zFa b2"cwӽ&zh^!ZC2y&@rE .W$ݺ}oo:FxHUU3KX"^mk ԫ]v:'wK Ƚ1F!@1[Yaה Hs>N%U4Ka=Ŧ PU'9<2^*yb\Q+̺_~B9V'Mt牷jg'K<ۜzwӏ֋[GhVdP媀W=D)BLP&B6HWhkQ 8#Ww&ѻ~ڼ v%oS&e"&^Zd8{!p弨t(]G!;{w25t,l; vzRE딝Rv;\MD*BWɊu`aZZԵ4fv.-'\`E<`Nw뮤[OFyQ ;>`(!S5/_nS@+f}i)y$v\aT=hEkrQ؆T=q,CdX's_\l:Rf2*-W{YOMGTJ+t!9T>Z墱&].;r:ӠaZ%XR0w)XrbŴa*:5Ak@A!3M &jIeG-OwQu7\pc]V]V4f^ b{hyvI>l<z9:2O,y)]cF6h{@6s3_o]6a L7Pr9ZŸlDG3uN#J. a٨wK݂EeŇd./ $fCѶg@6T$+b[iH{-+Oyf=wz;.)u=iG6_δy)o15Ldocxt8@rt,W9SώTS5@hbbpGtK_ 8o&$^' 5 RqA䎽6c_W2xFk8|jo-︤sp;ϑ `iX Ma!۱)áN^ev]*x4Cv, fO˹Mle TMVg:ےKi3k+G֞p\G- -%5SUY#6N oiY=泴ktk/^P^4=]"*Sʸ&|D |@D(/5>Z1r$M`lX D9"Pw|wDo9ϱ%^";E4gEFGx/ g&#0x lR GgrXn &hjmZYGYa:nD*F~mdGH.J',nâeeς<ֻ#cFj8kۨUpfgX3!ш"II;v"Xo=Rv JHi-CD/qXjf5n૑Hs@Y Yuw+_*d 3,W PEh<<T˘P7KU: Ia endstream endobj 170 0 obj << /Length 844 /Filter /FlateDecode >> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qhe9xSTQO7}̽][GLrFMG}}"Im!00jEV%H^/v}0<_7RyL U~C`PރSӯ|' imXYӷ|4nt.kP^k?IFsuB`nlB =@ )U 9yI(ѥ S*043``ÍSqf|kiCc, pDˆzF:x0)ljsn l9u}SrI4"nXCA8%&ٵ6AI cMXS?S/w,;: fyR(#c^g!ch"ƨ/kC^d cRx~h K^| МQV14Ld5cY9Y?=C9돡'g ?%>O:ShYg{ΈrYgDk>[bghX|&^V|sig33qgng3tZ[Yog,g-g B|B|\3gg|2?f)> stream xmUMo@Wla_BZX& Q+K62 5fI`x;fͳضߺItճ;סqT}s=ùֵA= }vu[Uyk֍I{wQ/5qDŽ r Gէn8A{,쏘LEvDB``B9zK~;_q`>Wgy o.>ݫﭯAbZ%?6G_Nzy;9ڰoiܰ^]0zu\~3ݍܥ: ل0%1 " 0Z{q́0R0r0QK5<T`,if,1gT Hӆp1X:,p8}u 8alSM3?r>x\i"EܰpJMkl4\?ǚc:#?^YHwuprQF^odž1BЖEQ?1^׆ƨАԗ039+ãbLi~jЙ}s~zrCOe fYJ|֟uМ8gΈrY׆}ŊϘъ1LҊkgigϘ݊og3f3|3ߊY[3 =L3f/gd ,' f)Rx jb&'W *.MGZN(:p~7a?}]TyԟE}Ư%Vu'e% endstream endobj 172 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3FXҝA5(O)suߖcHQIܮQW Lڮ9ˊ6nK5NoغWi~r<d(Vu;_=85vѩֆu5CNmm悥+U=#)\][|, MHS"#p #>y| #:##0)%T\`YQqJƚ`ci|1Mَbo4m `2WQ/cW888sέ-./qJ;&\ k(d?F#h0\?Ipa]~9Vk?q1Bx.BzҬÀhƘ'g 2xk=6u2,bق6E0F,eL燆LY` YecODV3Μ蛳;zr֟P.O0{S3ux9(uF: }6,V|ƌV|gegV|F_+>O+>G|V|~+>C1 V|B|FB|/g)g1{!>_|&~'a9i0K!cB{XTK5;)NŽbPq> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z endstream endobj 174 0 obj << /Length 841 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N)$̛3njNelܬ=WW\?;׺v=ݩqhY]jwOҺ=po]IAu~\3F;;=}kPQ/7SVk-8s擸]׷QPP]sݛ麟;l,j7O4uc׿x;P:XTv~{pjS! j֝ƍKWjY׫{FR^wwPG*X$D F @F@k} 89@FJuFF#`R0Jt 5 5553Tg_c.\Y7ь1O*ezl,d mY50ymȋ,aYʘ8 xA} /Y1<*T71މf 97g19w(g1?\֟`g Yg 9LsQ.(ulgYˊx/V|V|&٭ V|N+>cv+>7+>S} ~!>_Sϔ+>cB|&LOr`B,&+jwRP{x${y儢 1~g|sGӿNu]>~?,5(z6 endstream endobj 175 0 obj << /Length 844 /Filter /FlateDecode >> stream xmUMo@+H.Ȳ|h%Qի ۇ6웙7X=Tg ]NncT5ob<Y:socsOPcYB?9Os֙3\Q.4ڰX3Z9#>\Z} ?L[ V|V|oV|3[: } B|)W|L| ,Y a!SM~W,:?8C8…I^U E'b|82 8ϻ޽YtїkZbu_G4 endstream endobj 176 0 obj << /Length 843 /Filter /FlateDecode >> stream xmUMo0+J! ᫊"RVmk N7R!ݪ70W?g_,ɝиYs{ ]7;׺v=ߩǡoݨM'opiT}IAu~\3;he?<{Q%(SVk-#&9sQ擾ݾk^!00j(+m$?Gwt>X.oTuþ{S_tpСtZ|I1?H/'BZV;ݛ ZԲW/{FR^ww?U4H6!L@@B@q\s *G|F/+>㹴3Z~Z83f3[:٭ ߬Lg3t33 ~!>CO!>S 33>IY ?BXIAup*Çq G潪N$p|eO_:q;:'dE_kCa endstream endobj 177 0 obj << /Length 845 /Filter /FlateDecode >> stream xuUMo@+H.ȲrhQի C}͌6jo73o{q3fѭVO4cpuU sk/wOwquy_t}??p]AAu~\33cA}P>>%t;en>r8`S0Aj~vUk&Yos yv rOiHM0[7v,ܜǽJnkz~lNͿvt*amкq۸qۿ`J-ztH]{O|, MHS"#p #>y| #:##0)%T\`YQqJƚ`c2U{;5Ҵ!\,18"\aD E_sN[sS9)9^W$js7 GZ ׏p$uX}/S/w"': fyRy(#c^g!ch"ƨ-kC^d cRx~h K^| МQV14Nd5cY9Y?C9돡'g ?%>O:ShYggΈrYgDg>[bghX|&^V|{ig33qgng3tZ[Yog,g-g B|B|\3gg3?f)O5[TT+&GUP#a#7q/c?z~#袳rdbP)n endstream endobj 178 0 obj << /Length 700 /Filter /FlateDecode >> stream xuTMo0+J!m0U !mTto4j{zv|tv ںQf|6'op݅uM{}ugfci"Amƃ}>,%rtPRJ(:X'Ab~oںT7h uSӌ]Acq`sy̟M.n? D`އщ7+d~4Wj7vw VRŪ,ׁk/bxO0+,F )1!Pp #]QxQTv)#ZBYLt/X^r<1u%pr_d9٢PSi0@WQ_Uh֩h諵"qFM]RrCpt39Âж~j3Fezp888Q:1bc7~}Hq('bĄ>^m# &zd}4)` "H,4%!%AQ߄B[B~)ҙ́ _)M?DM;豬;kyoQnNRd\Ӎ;WA} zoZZgbT$Z|U endstream endobj 190 0 obj << /Producer (pdfTeX-1.40.24) /Author(\376\377\000N\000a\000m\000i\000t\000a\000\040\000G\000u\000p\000t\000a\000,\000\040\000S\000u\000s\000a\000n\000n\000a\000\040\000M\000a\000r\000q\000u\000e\000z\000,\000\040\000N\000i\000m\000a\000\040\000N\000o\000u\000r\000i\000\040\000a\000n\000d\000\040\000J\000u\000l\000i\000a\000n\000\040\000Q\000.\000\040\000Z\000h\000o\000u)/Title(\376\377\000S\000h\000a\000z\000a\000m\000:\000\040\000T\000u\000n\000i\000n\000g\000\040\000c\000l\000o\000n\000a\000l\000\040\000a\000s\000s\000i\000g\000n\000m\000e\000n\000t\000\040\000t\000h\000r\000e\000s\000h\000o\000l\000d\000s\000\040\000w\000i\000t\000h\000\040\000n\000e\000a\000r\000e\000s\000t\000\040\000n\000e\000i\000g\000h\000b\000o\000r\000\040\000d\000i\000s\000t\000a\000n\000c\000e\000s)/Subject()/Creator(\376\377\000L\000a\000T\000e\000X\000\040\000v\000i\000a\000\040\000p\000a\000n\000d\000o\000c)/Keywords() /CreationDate (D:20231002185727+02'00') /ModDate (D:20231002185727+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/CVE-2023-32700 patched) kpathsea version 6.3.4) >> endobj 144 0 obj << /Type /ObjStm /N 48 /First 417 /Length 3085 /Filter /FlateDecode >> stream xZYs8~c[*{rN&)?csG5$8݀(:"ofw_Ah|}’ЊLhìeVxzYόeN1aDw@i$JðQL:A0|NqLia$cʥ +*5N1)c3f4,Z2F;fLe*Ϭ΀ [ AXpgRO 1SeC:{2N˘OQv2/uK)Wx%qϼ@|Q[h dY XFİ23eS8ZeScI5=O~37|AP)@="i ֺt*?b?Rť VjF>Mk7E C~jdA楂,#T Dّ`e)!jH C[ <)ڰIIR {$OX07Bf/vpuYaC\vxnm%J؃7ߟsnFC1(`D#5uK|o4H+ "Q>=nNƐP&IO1@;AX'(j xd8Fk#c$H?Ф}hLk*%Ut0*pDUM@(fjBzZA+rPk({ S]/|K9RSU CR2L>jP0lL&1 u* (B3#,<4n=>I&:X+c7ֻNjF]-#ڢɠz JZ\(LQ#O:0MFvB5BZ'= -rioEAu:rZJAaTJI8mt#v/&eTέ@&Z a=O1ӻqmGOEaC= ~"HاfS@QvB:@F4Tc6թ}&!=Iq6$a}ApЏ-~uw'`q5o͸.mU|3g>W,49 7 Ӂc%/ |`)RW'0NǢ!%"F?oKO붘&0..P @⶘ob>->W#;GO(g=yU,y(M[ PTڇ)]ARbZc?_ V.W/1oڢ.tZ,F,)JWzߣ-T6&Q!M1+þMX5oh-ooj4)jSw>{ݯ?YaIF@woz?٫'2TKP{PZ;<"sP>Pxɓ*R4s=BePMTf_<}>TwDƛrO&>ɻ/ռh ,-*nx:vH8G֗GW?;{y|f#I)PS(e[N'[gu>h)~<7pA+|n"xEӬ|9.ꦼ5":+]L x˪-&E|8ڣ|6=֐Ekd5$XaCkA?*u*uhĎh>&)?|(`qѰߤ!2VVٙHOhѦ-Fx`h(;ဖNIN@=e!_U"'GaVk~ͭ_)Bpyphg*p_BRP6MWn ^nZ&E_ZNۆ"ڕm\Vo栓 ۓɺ.0 e>Y!!*nC\7=F\yqqBWW# j({ULj/ޚm(얗aKkdGj/"l(얛)I#{k[N>3a6&@˫-yݶ;hx!86~#O)'Z.zhčX_`1M)!kEb!w,ylP4IH -tڲIHww]Dž/#LmXz=:[4eּA^k\r+=K ^z/u;lx5/}~/%v7$^;<6S׃F0Lw |J+{2)bܖO>m9)~_ox5nd|j{OY,X_Lc9s)&>$OwؔxLt,Q$%a[hY,;g꿧0 Rh] $ˤOI{?oMvxN+ݢSe=Vnh0s1M P*5DV;d[vs @t0`51ƹ} EU e͢(H OeSt@˻3jސOJ4x͐V9I?9aCNEX6_1 a\f/:prP2>U|4iEPj>a3nNB.VT/ W x iuͺ'U endstream endobj 191 0 obj << /Type /XRef /Index [0 192] /Size 192 /W [1 3 1] /Root 189 0 R /Info 190 0 R /ID [ ] /Length 518 /Filter /FlateDecode >> stream x%oLas;L[JFfPoҢ*UVovJ"66]FXXLB7l$ _`!!hHH$Z<Dz&T HATW-'`Paۣp #*OnS02I4U 0h۟ ?.%Z(\_6mTT '0RlHe#F(Q*}ZUFҟpW yUInܫ&S^4B48+%*AJ#>F|$H0#֩ɟr|LYc endstream endobj startxref 265640 %%EOF shazam/inst/doc/DistToNearest-Vignette.Rmd0000644000176200001440000004110714367147774020265 0ustar liggesusers--- title: 'Shazam: Tuning clonal assignment thresholds with nearest neighbor distances' author: "Namita Gupta, Susanna Marquez, Nima Nouri and Julian Q. Zhou" date: '`r Sys.Date()`' output: pdf_document: dev: pdf fig_height: 4 fig_width: 7.5 highlight: pygments toc: yes html_document: fig_height: 4 fig_width: 7.5 highlight: pygments theme: readable toc: yes geometry: margin=1in fontsize: 11pt vignette: > %\VignetteIndexEntry{Distance to nearest neighbor} %\usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- Estimating the optimal distance threshold for partitioning clonally related sequences is accomplished by calculating the distance from each sequence in the data set to its nearest neighbor and finding the break point in the resulting bi-modal distribution that separates clonally related from unrelated sequences. This is done via the following steps: 1. Calculating of the nearest neighbor distances for each sequence. 2. Generating a histogram of the nearest neighbor distances followed by either manual inspect for the threshold separating the two modes or automated threshold detection. ## Example data A small example AIRR Rearrangement database is included in the `alakazam` package. Calculating the nearest neighbor distances requires the following fields (columns) to be present in the table: * `sequence_id` * `v_call` * `j_call` * `junction` * `junction_length` ```{r, eval=TRUE, warning=FALSE, message=FALSE} # Import required packages library(alakazam) library(dplyr) library(ggplot2) library(shazam) # Load and subset example data (for speed) data(ExampleDb, package="alakazam") set.seed(112) db <- ExampleDb %>% sample_n(size=500) db %>% count(sample_id) ``` ## Calculating nearest neighbor distances (heavy chain sequences) By default, `distToNearest`, the function for calculating distance between every sequence and its nearest neighbor, assumes that it is running under non-single-cell mode and that every input sequence is a heavy chain sequence and will be used for calculation. It takes a few parameters to adjust how the distance is measured. * If a genotype has been inferred using the methods in the `tigger` package, and a `v_call_genotyped` field has been added to the database, then this column may be used instead of the default `v_call` column by specifying the `vCallColumn` argument. * This will allows the more accurate V call from `tigger` to be used for grouping of the sequences. * Furthermore, for more leniency toward ambiguous V(D)J segment calls, the parameter `first` can be set to `FALSE`. * Setting `first=FALSE` will use the union of all possible genes to group sequences, rather than the first gene in the field. * The `model` parameter determines which underlying SHM model is used to calculate the distance. * The default model is single nucleotide Hamming distance with gaps considered as a match to any nucleotide (`ham`). * Other options include a human Ig-specific single nucleotide model similar to a transition/transversion model (`hh_s1f`) and the corresponding 5-mer context model from Yaari et al, 2013 (`hh_s5f`), an analogous pair of mouse specific models from Cui et al, 2016 (`mk_rs1nf` and `mk_rs5nf`), and amino acid Hamming distance (`aa`). **Note:** Human and mouse distance measures that are backward compatible with SHazaM v0.1.4 and Change-O v0.3.3 are also provided as `hs1f_compat` and `m1n_compat`, respectively. For models that are not symmetric (e.g., distance from A to B is not equal to the distance from B to A), there is a `symmetry` parameter that allows the user to specify whether the average or minimum of the two distances is used to determine the overall distance. ```{r, eval=TRUE, warning=FALSE} # Use nucleotide Hamming distance and normalize by junction length dist_ham <- distToNearest(db %>% filter(sample_id == "+7d"), sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # Use genotyped V assignments, a 5-mer model and no normalization dist_s5f <- distToNearest(db %>% filter(sample_id == "+7d"), sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="hh_s5f", normalize="none", nproc=1) ``` ## Calculating nearest neighbor distances (single-cell paired heavy and light chain sequences) The `distToNearest` function also supports running under single-cell mode where an input `Example10x` containing single-cell paired IGH:IGK/IGL, TRB:TRA, or TRD:TRG chain sequences are supplied. In this case, by default, cells are first divided into partitions containing the same heavy/long chain (IGH, TRB, TRD) V gene and J gene (and if specified, junction length), and the same light/short chain (IGK, IGL, TRA, TRG) V gene and J gene (and if specified, junction length). Then, only the heavy chain sequences are used for calculating the nearest neighbor distances. Under the single-cell mode, each row of the input `Example10x` should represent a sequence/chain. Sequences/chains from the same cell are linked by a cell ID in a `cellIdColumn` column. Note that a cell should have exactly one `IGH` sequence (BCR) or `TRB`/`TRD` (TCR). The values in the `locusColumn` column must be one of `IGH`, `IGI`, `IGK`, or `IGL` (BCR) or `TRA`, `TRB`, `TRD`, or `TRG` (TCR). To invoke the single-cell mode, `cellIdColumn` must be specified and `locusColumn` must be correct. There is a choice of whether grouping should be done as a one-stage process or a two-stage process. This can be specified via `VJthenLen`. * In the one-stage process (`VJthenLen=FALSE`), cells are divided into partitions containing same heavy/long chain V gene, J gene, and junction length (V-J-length combination), and the same light chain V-J-length combination. * In the two-stage process (`VJthenLen=TRUE`), cells are first divided by heavy/long chain V gene and J gene (V-J combination), and light/short chain V-J combination; and then by the corresponding junction lengths. There is also a choice of whether grouping should be done using `IGH` (BCR) or `TRB/TRD` (TCR) sequences only, or using both `IGH` and `IGK`/`IGL` (BCR) or `TRB`/`TRD` and `TRA`/`TRG` (TCR) sequences. This is governed by `onlyHeavy`. ```{r, eval=FALSE, warning=FALSE} # Single-cell mode # Group cells in a one-stage process (VJthenLen=FALSE) and using # both heavy and light chain sequences (onlyHeavy=FALSE) data(Example10x, package="alakazam") dist_sc <- distToNearest(Example10x, cellIdColumn="cell_id", locusColumn="locus", VJthenLen=FALSE, onlyHeavy=FALSE) ``` Regardless of whether grouping was done using only the heavy chain sequences, or both heavy and light chain sequences, only heavy chain sequences will be used for calculating the nearest neighbor distances. Hence, under the single-cell mode, rows in the returned `data.frame` corresponding to light chain sequences will have `NA` in the `dist_nearest` field. ## Using nearest neighbor distances to determine clonal assignment thresholds The primary use of the distance to nearest calculation in SHazaM is to determine the optimal threshold for clonal assignment using the `DefineClones` tool in Change-O. Defining a threshold relies on distinguishing clonally related sequences (represented by sequences with close neighbors) from singletons (sequences without close neighbors), which show up as two modes in a nearest neighbor distance histogram. Thresholds may be manually determined by inspection of the nearest neighbor histograms or by using one of the automated threshold detection algorithms provided by the `findThreshold` function. The available methods are `density` (smoothed density) and `gmm` (gamma/Gaussian mixture model), and are chosen via the `method` parameter of `findThreshold`. ### Threshold determination by manual inspection Manual threshold detection simply involves generating a histrogram for the values in the `dist_nearest` column of the `distToNearest` output and selecting a suitable value within the valley between the two modes. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate Hamming distance histogram p1 <- ggplot(subset(dist_ham, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + labs(x = "Hamming distance", y = "Count") + scale_x_continuous(breaks=seq(0, 1, 0.1)) + theme_bw() plot(p1) ``` By manual inspection, the length normalized `ham` model distance threshold would be set to a value near 0.12 in the above example. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate HH_S5F distance histogram p2 <- ggplot(subset(dist_s5f, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=1) + geom_vline(xintercept=7, color="firebrick", linetype=2) + labs(x = "HH_S5F distance", y = "Count") + scale_x_continuous(breaks=seq(0, 50, 5)) + theme_bw() plot(p2) ``` In this example, the unnormalized `hh_s5f` model distance threshold would be set to a value near 7. ### Automated threshold detection via smoothed density The `density` method will look for the minimum in the valley between two modes of a smoothed distribution based on the input vector (`distances`), which will generally be the `dist_nearest` column from the `distToNearest` output. Below is an example of using the `density` method for threshold detection. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Find threshold using density method output <- findThreshold(dist_ham$dist_nearest, method="density") threshold <- output@threshold # Plot distance histogram, density estimate and optimum threshold plot(output, title="Density Method") # Print threshold print(output) ``` ### Automated threshold detection via a mixture model The `findThreshold` function includes approaches for automatically determining a clonal assignment threshold. The `"gmm"` method (gamma/Gaussian mixture method) of `findThreshold` (`method="gmm"`) performs a maximum-likelihood fitting procedure over the distance-to-nearest distribution using one of four combinations of univariate density distribution functions: `"norm-norm"` (two Gaussian distributions), `"norm-gamma"` (lower Gaussian and upper gamma distribution), `"gamma-norm"` (lower gamm and upper Gaussian distribution), and `"gamma-gamma"` (two gamma distributions). By default, the threshold will be selected by calculating the distance at which the average of sensitivity and specificity reaches its maximum (`cutoff="optimal"`). Alternative threshold selection criteria are also providing, including the curve intersection (`cutoff="intersect"`), user defined sensitivity (`cutoff="user", sen=x`), or user defined specificity (`cutoff="user", spc=x`) In the example below the mixture model method (`method="gmm"`) is used to find the optimal threshold for separating clonally related sequences by fitting two gamma distributions (`model="gamma-gamma"`). The red dashed-line shown in figure below defines the distance where the average of the sensitivity and specificity reaches its maximum. ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Find threshold using gmm method output <- findThreshold(dist_ham$dist_nearest, method="gmm", model="gamma-gamma") # Plot distance histogram, Gaussian fits, and optimum threshold plot(output, binwidth=0.02, title="GMM Method: gamma-gamma") # Print threshold print(output) ``` **Note:** The shape of histogram plotted by `plotGmmThreshold` is governed by the `binwidth` parameter. Meaning, any change in bin size will change the form of the distribution, while the `gmm` method is completely bin size independent and only engages the real input data. ## Calculating nearest neighbor distances independently for subsets of data The `fields` argument to `distToNearest` will split the input `data.frame` into groups based on values in the specified fields (columns) and will treat them independently. For example, if the input data has multiple samples, then `fields="sample_id"` would allow each sample to be analyzed separately. In the previous examples we used a subset of the original example data. In the following example, we will use the two available samples, `-1h` and `+7d`, and will set `fields="sample_id"`. This will reproduce previous results for sample `+7d` and add results for sample `-1d`. ```{r fields, eval=TRUE, warning=FALSE} dist_fields <- distToNearest(db, model="ham", normalize="len", fields="sample_id", nproc=1) ``` We can plot the nearest neighbor distances for the two samples: ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate grouped histograms p4 <- ggplot(subset(dist_fields, !is.na(dist_nearest)), aes(x=dist_nearest)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + labs(x = "Grouped Hamming distance", y = "Count") + facet_grid(sample_id ~ ., scales="free_y") + theme_bw() plot(p4) ``` In this case, the threshold selected for `+7d` seems to work well for `-1d` as well. ## Calculating nearest neighbor distances across groups rather than within a groups Specifying the `cross` argument to `distToNearest` forces distance calculations to be performed across groups, such that the nearest neighbor of each sequence will always be a sequence in a different group. In the following example we set `cross="sample"`, which will group the data into `-1h` and `+7d` sample subsets. Thus, nearest neighbor distances for sequences in sample `-1h` will be restricted to the closest sequence in sample `+7d` and vice versa. ```{r cross, eval=TRUE, warning=FALSE} dist_cross <- distToNearest(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, normalize="len", cross="sample_id", nproc=1) ``` ```{r, eval=TRUE, warning=FALSE, fig.width=7} # Generate cross sample histograms p5 <- ggplot(subset(dist_cross, !is.na(cross_dist_nearest)), aes(x=cross_dist_nearest)) + labs(x = "Cross-sample Hamming distance", y = "Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") + theme_bw() plot(p5) ``` This can provide a sense of overlap between samples or a way to compare within-sample variation to cross-sample variation. ## Speeding up pairwise-distance-matrix calculations with subsampling The `subsample` option in `distToNearest` allows to speed up calculations and reduce memory usage. If there are very large groups of sequences that share V call, J call and junction length, `distToNearest` will need a lot of memory and it will take a long time to calculate all the distances. Without subsampling, in a large group of n=70,000 sequences `distToNearest` calculates a n\*n distance matrix. With subsampling, e.g. to s=15,000, the distance matrix for the same group has size s\*n, and for each sequence in `db`, the distance value is calculated by comparing the sequence to the subsampled sequences from the same V-J-junction length group. ```{r subsample, eval=TRUE, warning=FALSE} # Explore V-J-junction length groups sizes to use subsample # Show the size of the largest groups top_10_sizes <- ExampleDb %>% group_by(junction_length) %>% # Group by junction length do(alakazam::groupGenes(., first=TRUE)) %>% # Group by V and J call mutate(GROUP_ID=paste(junction_length, vj_group, sep="_")) %>% # Create group ids ungroup() %>% group_by(GROUP_ID) %>% # Group by GROUP_ID distinct(junction) %>% # Count unique junctions per group summarize(SIZE=n()) %>% # Get the size of the group arrange(desc(SIZE)) %>% # Sort by decreasing size select(SIZE) %>% top_n(10) # Filter to the top 10 top_10_sizes # Use 30 to subsample # NOTE: This is a toy example. Subsampling to 30 sequence with real data is unwise dist <- distToNearest(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="ham", first=FALSE, normalize="len", subsample=30) ``` shazam/inst/CITATION0000644000176200001440000000656313670240241013660 0ustar liggesusersbibentry(bibtype = "Article", style = "citation", header = "To cite the SHazaM package in publications, please use:", title = "Change-O: a toolkit for analyzing large-scale B cell immunoglobulin repertoire sequencing data.", author = c(person("Namita T.", "Gupta"), person("Jason A.", "Vander Heiden"), person("Mohamed", "Uduman"), person("Daniel", "Gadala-Maria"), person("Gur", "Yaari"), person("Steven H.", "Kleinstein")), year = 2015, journal = "Bioinformatics", pages = "1-3", doi = "10.1093/bioinformatics/btv359") bibentry(bibtype = "Article", style = "citation", header = "To cite the selection analysis methods, please use:", title = "Quantifying selection in high-throughput Immunoglobulin sequencing data sets.", author = c(person("Gur", "Yaari"), person("Mohamed", "Uduman"), person("Steven H.", "Kleinstein")), year = 2012, journal = "Nucleic acids research", volume = 40, number = 17, pages = "e134", doi = "10.1093/nar/gks457") bibentry(bibtype = "Article", style = "citation", header = "To cite the HH_S5F model and the targeting model generation methods, please use:", title = "Models of somatic hypermutation targeting and substitution based on synonymous mutations from high-throughput immunoglobulin sequencing data.", author = c(person("Gur", "Yaari"), person("Jason A.", "Vander Heiden"), person("Mohamed", "Uduman"), person("Daniel", "Gadala-Maria"), person("Namita T.", "Gupta"), person("Joel N. H.", "Stern"), person("Kevin C.", "O'Connor"), person("David A.", "Hafler"), person("Uri", "Lasserson"), person("Francois", "Vigneault"), person("Steven H.", "Kleinstein")), year = 2013, journal = "Frontiers in Immunology", volume = 4, number = 358, pages = "1-11", doi = "10.3389/fimmu.2013.00358") bibentry(bibtype = "Article", style = "citation", header = "To cite the HKL_S1F, HKL_S5F, MK_RS1NF, and MK_RS5NF models, please use:", title = "A Model of Somatic Hypermutation Targeting in Mice Based on High-Throughput Ig Sequencing Data.", author = c(person("Ang", "Cui"), person("Roberto", "Di Niro"), person("Jason A.", "Vander Heiden"), person("Adrian W.", "Briggs"), person("Kris", "Adams"), person("Tamara", "Gilbert"), person("Kevin C.", "O'Connor"), person("Francois", "Vigneault"), person("Mark J.", "Shlomchik"), person("Steven H.", "Kleinstein")), year = 2016, journal = "The Journal of Immunology", volume = 197, number = 9, pages = "3566-3574", doi = "10.4049/jimmunol.1502263")