shazam/0000755000176200001440000000000014071672543011546 5ustar liggesusersshazam/NAMESPACE0000644000176200001440000001217214071640643012764 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(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(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,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,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(kedd,h.ucv) importFrom(lazyeval,interp) importFrom(progress,progress_bar) 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,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,spread) importFrom(tidyselect,all_of) shazam/README.md0000644000176200001440000000513214053000607013010 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/0000755000176200001440000000000013763230460012452 5ustar liggesusersshazam/data/MK_RS5NF.rda0000644000176200001440000047261613752621627014415 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.rda0000644000176200001440000014113413752621627013564 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.rda0000644000176200001440000000170113752621627015544 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.rda0000644000176200001440000000102313752621627014172 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.rda0000644000176200001440000043340513752621627014255 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.rda0000644000176200001440000000101713763230460014404 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.rda0000644000176200001440000000174213752621627015627 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.rda0000644000176200001440000000112013752621627015750 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.rda0000644000176200001440000000101113752621627016066 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.rda0000644000176200001440000000170513752621627016312 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/0000755000176200001440000000000014067121426012313 5ustar liggesusersshazam/man/plotMutability.Rd0000644000176200001440000000444313754032715015635 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.Rd0000644000176200001440000001152113754032715016567 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{ \donttest{ # Subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) # 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.Rd0000644000176200001440000000530314067611333014755 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.Rd0000644000176200001440000001040713754032715016607 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 data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) # 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) # Facet by group instead of region plotBaselineSummary(grouped, "sample_id", "c_call", facetBy="group") } } \seealso{ Takes as input either a \link{Baseline} object returned by \link{groupBaseline} or a data.frame returned from \link{summarizeBaseline}. } shazam/man/groupBaseline.Rd0000644000176200001440000000756213754032715015417 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{ \donttest{ # Subset example data from alakazam data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG")) # 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.Rd0000644000176200001440000000315713754032715016263 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.Rd0000644000176200001440000000730014067121426014511 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.Rd0000644000176200001440000000451113754032715016617 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.Rd0000644000176200001440000000412613754032715017033 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/DensityThreshold-class.Rd0000644000176200001440000000223413754032715017206 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.Rd0000644000176200001440000001136413754032715014425 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 sample as demos data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA") 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(5, 100)) # 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.Rd0000644000176200001440000001032213754032715015512 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.Rd0000644000176200001440000002530313754032715017111 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.Rd0000644000176200001440000000360313754032715016310 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.Rd0000644000176200001440000002736313754032715015411 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", 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. Only applicable to single-cell data. Ignored if \code{cellIdColumn=NULL}. Valid loci values are "IGH", "IGI", "IGK", "IGL", "TRA", "TRB", "TRD", and "TRG".} \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. } shazam/man/calcExpectedMutations.Rd0000644000176200001440000000650613754032715017105 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.Rd0000644000176200001440000000225013754032715014031 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.Rd0000644000176200001440000000466513754032715017014 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.Rd0000644000176200001440000000254713754032715017727 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.Rd0000644000176200001440000000360313754032715015346 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, windowSize ) } \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.} } \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) } \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.Rd0000644000176200001440000000122713754032715017024 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.Rd0000644000176200001440000000106113754032715013213 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/makeDegenerate5merSub.Rd0000644000176200001440000000406213754032715016754 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.Rd0000644000176200001440000001703714067625624016340 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.Rd0000644000176200001440000000572414067611101016557 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.Rd0000644000176200001440000000175613754032715013573 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.Rd0000644000176200001440000000214113754032715017520 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.Rd0000644000176200001440000000230314067121426015207 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{http://www.imgt.org/IMGTeducation/Aide-memoire/_UK/aminoacids/IMGTclasses.html} } } shazam/man/expectedMutations.Rd0000644000176200001440000001004214053000756016301 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") # 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.Rd0000644000176200001440000000535713754032715015242 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 data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call \%in\% c("IGHM", "IGHG", "IGHA")) # 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.Rd0000644000176200001440000000704514010044136015722 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 ) } \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}.} } \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}. } \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.Rd0000644000176200001440000000773013754032715016706 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") # 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.Rd0000644000176200001440000000211413754032715013673 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.Rd0000644000176200001440000000207513754032715017151 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.Rd0000644000176200001440000000276213754032715016300 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.Rd0000644000176200001440000000402413754032715017326 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") # 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.Rd0000644000176200001440000005154414067121426015561 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.Rd0000644000176200001440000000341713754032715015554 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) } \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.Rd0000644000176200001440000000457613754032715016116 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.Rd0000644000176200001440000001275614067122435014101 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.Rd0000644000176200001440000000652413754032715016620 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=100, 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.Rd0000644000176200001440000000405013754032715016765 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.Rd0000644000176200001440000000201613754032715016606 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.Rd0000644000176200001440000001072113754032715016572 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/MutationProfiling.R \name{slideWindowTunePlot} \alias{slideWindowTunePlot} \title{Visualize parameter tuning for sliding window approach} \usage{ slideWindowTunePlot( tuneList, plotFiltered = TRUE, 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 ) } \arguments{ \item{tuneList}{a list of logical matrices returned by \link{slideWindowTune}.} \item{plotFiltered}{whether to plot the number of filtered sequences (as opposed to the number of remaining sequences). 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)} } \description{ Visualize results from \link{slideWindowTune} } \details{ For each \code{windowSize}, the numbers of sequences filtered or remaining after applying the sliding window approach are plotted on the y-axis against thresholds on the number of mutations in a window on the x-axis. 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.Rd0000644000176200001440000001204013754032715017277 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 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") 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.Rd0000644000176200001440000000556713754032715016302 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") # 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.Rd0000644000176200001440000000210613754032715017257 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.Rd0000644000176200001440000001342413754032715015407 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} automtically 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 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", jCallColumn="j_call", model="ham", normalize="len", nproc=1) # Find threshold using the "gmm" method with optimal threshold output <- findThreshold(db$dist_nearest, method="gmm", model="gamma-gamma", cutoff="opt") plot(output, binwidth=0.02, title=paste0(output@model, " loglk=", output@loglk)) print(output) # 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) # Find threshold using the "density" method and plot the results output <- findThreshold(db$dist_nearest, method="density") plot(output) 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.Rd0000644000176200001440000000617713754032715015263 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.Rd0000644000176200001440000000631213754032715017264 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") # 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.Rd0000644000176200001440000000203213754032715013553 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.Rd0000644000176200001440000000222713754032715014031 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.Rd0000644000176200001440000001343613754032715017702 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") # 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.Rd0000644000176200001440000000201013754032715013672 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.Rd0000644000176200001440000000204213754032715017013 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.Rd0000644000176200001440000000261413754032715017606 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 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.Rd0000644000176200001440000000643613754032715015444 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/calcBaseline.Rd0000644000176200001440000001327714010044136015147 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.Rd0000644000176200001440000000163713754032715017235 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.Rd0000644000176200001440000000240713754032715015201 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 data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHG" & sample_id == "+7d") # 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.Rd0000644000176200001440000000545013754032715017107 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 one isotype and sample as a demo data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") # 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.Rd0000644000176200001440000000451113754032715015102 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.Rd0000644000176200001440000001043114053000756016307 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/DESCRIPTION0000644000176200001440000000556214071672542013263 0ustar liggesusersPackage: shazam Type: Package Version: 1.1.0 Date: 2021-07-08 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"), 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("Ang", "Cui", role=c("ctb"), email="angcui@mit.edu"), person("Jason", "Vander Heiden", role=c("aut", "cre"), 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 Encoding: UTF-8 Depends: R (>= 3.5.0), ggplot2 (>= 3.3.4) Imports: alakazam (>= 1.1.0), ape, diptest, doParallel, dplyr (>= 0.8.1), foreach, graphics, grid, igraph, iterators, kedd, 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' 'DistToNearest.R' 'MutationDefinitions.R' 'MutationProfiling.R' 'RegionsExtend.R' 'Shmulate.R' 'TargetingModels.R' RoxygenNote: 7.1.1 NeedsCompilation: no Packaged: 2021-07-08 18:21:52 UTC; vandej27 Author: Mohamed Uduman [aut], Namita Gupta [aut], Susanna Marquez [aut], Julian Zhou [aut], Nima Nouri [aut], Ang Cui [ctb], Jason Vander Heiden [aut, cre], Gur Yaari [aut], Steven Kleinstein [aut, cph] Maintainer: Jason Vander Heiden Repository: CRAN Date/Publication: 2021-07-08 22:00:02 UTC shazam/build/0000755000176200001440000000000014071641077012643 5ustar liggesusersshazam/build/vignette.rds0000644000176200001440000000054314071641077015204 0ustar liggesusersSJ1MڂԊB~~"HE"m ,o`Ͷj9ɜ=}2ZpFMm7h|ǭ0('*Y0vr46 VhYVH&hJhUy_xS㗥BP1rtHg/:A\a\Xޓy2N1g:Ai8O{L1&~C&hU^/c@ t< <߬5dHjds$$a"gxF_q09:wqP';*W7b Q4w] zshazam/vignettes/0000755000176200001440000000000014071641077013554 5ustar liggesusersshazam/vignettes/Shmulate-Vignette.html0000644000176200001440000264137314070613433020021 0ustar liggesusers PDF-1.5

shazam/vignettes/Mutation-Vignette.Rmd0000644000176200001440000002262614071631673017614 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, may be 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(shazam) library(dplyr) library(ggplot2) # 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 a 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)) + theme_bw() + ggtitle("Total mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() 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 subdivisons 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)) + theme_bw() + ggtitle("CDR silent mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() g3 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_r, fill=c_call)) + theme_bw() + ggtitle("CDR replacement mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() alakazam::gridPlot(g2, g3, ncol=2) ``` ## Use amino acid physicochemical properties to define mutations By default, replacement and silent are determined by exact amino acid identity. But 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 mutation 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)) + theme_bw() + ggtitle("Charge replacement mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() plot(g4) ``` shazam/vignettes/Baseline-Vignette.Rmd0000644000176200001440000004120214071626671017527 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} # Load example data library(shazam) library(alakazam) data(ExampleDb, package="alakazam") ``` ## 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=TRUE, 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 hydrophobicitity 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 <- 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 analagous 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, one needs to edit the existing values in a field in a `Baseline` object, one 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, one 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.Rmd0000644000176200001440000001770114071626247017737 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} # Load example data library(shazam) 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.Rmd0000644000176200001440000001432014067611263017564 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} 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} library(alakazam) library(shazam) library(igraph) # 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 exmample, 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.Rmd0000644000176200001440000004014314063420465020532 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: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Distance to nearest neighbor} %\usepackage[utf8]{inputenc} --- 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} # Subset example data to one sample library(shazam) data(ExampleDb, package="alakazam") ``` ## 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(ExampleDb, 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(ExampleDb, 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=TRUE, 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 library(ggplot2) p1 <- ggplot(subset(dist_ham, !is.na(dist_nearest)), aes(x=dist_nearest)) + theme_bw() + xlab("Hamming distance") + ylab("Count") + scale_x_continuous(breaks=seq(0, 1, 0.1)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) 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)) + theme_bw() + xlab("HH_S5F distance") + ylab("Count") + scale_x_continuous(breaks=seq(0, 50, 5)) + geom_histogram(color="white", binwidth=1) + geom_vline(xintercept=7, color="firebrick", linetype=2) 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 `-1h` and add results for sample `+7d`. ```{r fields, eval=TRUE, warning=FALSE} dist_fields <- distToNearest(ExampleDb, 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)) + theme_bw() + xlab("Grouped Hamming distance") + ylab("Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") plot(p4) ``` In this case, the threshold selected for `-1h` seems to work well for `+7d` 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)) + theme_bw() + xlab("Cross-sample Hamming distance") + ylab("Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") 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 library(dplyr) library(alakazam) 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) %>% # Vount 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/0000755000176200001440000000000014071345330011736 5ustar liggesusersshazam/R/MutationDefinitions.R0000644000176200001440000001702214067120230016052 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 #' 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{http://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.R0000644000176200001440000027660714067117207013632 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 #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHG" & sample_id == "+7d") #' #' # 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 #' \donttest{ #' # Subset example data from alakazam #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' #' # 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) gc() # 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) gc() # 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") #' #' # 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 #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG", "IGHA")) #' #' # 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 #' \donttest{ #' # Subset example data #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' #' # 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", size=0.5)) + theme(strip.background=element_rect(fill="white", color="black", size=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_string(x="SIGMA", y="DENSITY")) + base_theme + xlab(expression(Sigma)) + ylab("Density") + geom_line(aes(size=size)) # Add line if (colorElement == "id" & is.null(secondaryColumn)) { p1 <- p1 + aes_string(color=idColumn) } else if (colorElement == "id" & !is.null(secondaryColumn)) { p1 <- p1 + aes_string(color=idColumn, linetype=secondaryColumn) } else if (colorElement == "group") { p1 <- p1 + aes_string(color=secondaryColumn, linetype=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(size="none", colour = guide_legend(override.aes=list(size=size_values))) if (length(unique(c(groupColumn, idColumn))) > 1) { p1 <- p1 + guides (linetype=guide_legend(override.aes=list(size=size_values))) } } else if (sizeElement == colorElement) { p1 <- p1 + guides(size="none", colour = guide_legend(override.aes = list(size = size_values))) } else { p1 <- p1 + guides(size="none", linetype = guide_legend(override.aes = list(size = 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 #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call %in% c("IGHM", "IGHG")) #' #' # 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) #' #' # Facet by group instead of region #' plotBaselineSummary(grouped, "sample_id", "c_call", facetBy="group") #' } #' #' @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", size=0.5)) + theme(strip.background=element_rect(fill="white", color="black", size=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_string(x=idColumn, y="baseline_sigma", ymax=max("baseline_sigma"))) + base_theme + xlab("") + ylab(expression(Sigma)) + geom_hline(yintercept=0, size=1*size, linetype=2, color="grey") + geom_point(size=3*size, position=position_dodge(0.6)) + geom_errorbar(aes_string(ymin="baseline_ci_lower", ymax="baseline_ci_upper"), width=0.2, size=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_string(color=groupColumn) + facet_grid(region ~ .) } else if (!is.null(groupColumn) & is.null(groupColors) & facetBy == "region") { p1 <- p1 + aes_string(color=groupColumn) + facet_grid(region ~ .) } else if (!is.null(groupColumn) & facetBy == "group") { p1 <- p1 + scale_color_manual(name="Region", values=REGION_PALETTE) + aes_string(color="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) } 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.rda0000644000176200001440000014135313752621627014120 0ustar liggesusersBZh91AY&SYS>jB^!< $ cIYipXA*j_[4:SVVMvp.@ZTmT  &Du@0lCAÈY H3`!PJ[Rmn ܕpS,v`lWvoq0ې8 ]ؔxrS{`CVf ,d`t4 p`ttAtW7` ;yp{0@(CGFπ;v ;;0P D " @!@H#Ah44C )BmF`4MzjzOSii<i0L4h &0` +RE=MJ%MSFzy mOd4 hh 44SЀ)BA@ 4Ɉhh UUҕ*zz4(  OFFFC h4h4ddɦ 24hA&ihɡC2 44 4 FDxALɠh M4U߳?$ZS NM )14N-M9se622-DR1Bu}VM :_: d;2k(SPٛR;7z8%X[=y4Og|j F #[=Y!%fnxP*)2Jjg-1ו7R^eaߚosZzŢ[vO',B:}7XCkUA3GRlFz=d4>xG G@vǓo{ȎN]y>ws/TvBiuY Q_/kÒ仇F# I{P\T`MCͱ~1ɛvO1F궀xaqsueŷYnf0e\*ZVţ3nmkS b)-.L, doVM3&kSr~z^3ҋ\5؄pv`e>5oNW˥gP:/.)o6h7uƜ<#_91!?9}6d/DKsBÜf: .yǬnPqW] Kܼ]wzJ0+҈,%;~ۻREˇM[[W)뗯]QV&m^:ɿ+ڴAjQ!>ʪgu.5A)q"ZԚ=^Voc0 i Oe,Er g)qiEiB*+E;ZdҋXf(ϝ ʈɞ[2u{j{ϽuZ;+<9-TL{4_[IF ͲlXK˥҆GR{q\XJm}S;Rc*-q 抱zSmǠ7eyyI Oy !E ?1q*A'kFئ#EC2coi5I7Ȏs n4.|ҘJ'JM(.,wAZוM;jXTKl:{X̾n%kZn<>< Tg})>xnzz}R/_0=ƫl-7i8eOh4'5YҌ___Չ&nɔ,*bke4x䣽Q}(Rεk^4Y~R3NYfk0ΑĝAYJujG}s86{R!j\ows?5nVT^J2¶D3TIN#tkIzou뮻_7[:zn\ADNTЩW:*Өa懴g!e_ D/Mu y!qri8W7y%OSg >5Z)ufIO)~M)]פ\iR)*NBPdsϜGoZkz y"צ/ih"8ZXpOsqmoOn<+͢sqH1 У֘^kJX-\LO>$+q|=c1zxHBP0PnT%7!oK˞wXS 4洍=8HϤZ&*4G/j&8Ҷ5%Fռ1rp;G eoc x1̄L [cûvVi[ZTzjq|6^ ka,"x_ &f^.9ӍLO+vqXn+P lˍ)F=uyyzGґ][M{h}fuU3^ѷY!TcδA:ROY׭{aRE%.^wsk<|_PiWKOx>ﭔ S,s+JKGu\gi*%G?zi6Smv˫ۧgFլkͪb,Eq\=Nr$iH˅<>6IQf ֯U}ܧu!)K5^-O%oo'>[9P-=ELw geJ`w]au?ŊgQ$GU޽OdM -oY[/ G:fŤ nP6ߐ$Y{JЎ\]p" $qFV50rWZ} T扻׌_ĆGUqh=t7290tv:lgYy兇`"~H?9D`FTN9xS_'{qx5쎒XU_xc^9pYƯ>_tK:Ff G pzkíW+McQ)PLẝrGqE~A0.sYoܛqHjtcUՁ4v̼S|Ѽ}ߔ{ |m.X[}Vꯆ]oUynT= h(Y)vN$"?7˦_$A~.m ]q)E=_ZHO º㘋>21e"UꃰSPG{[15Z!piVnϳBSujK(aPg$yTjP4HɻЂ>\f3l_/A={X2VOׯSu"DP7vDC2_#\>,&mf4p{+/;o3N6V70XLN )u.$Q{SΫks8lP+4X߽x/v +|%!6nJH]di>eݺ|JVX侧q_)vݵ$,2+xwR6jv!.սѻ/'{J8F-\s_g؃P;rGl`d$ʡYv]rۯ7eW"lb}>E8[>N؄{wyB &&i|[/HSJ#Yd™v4>9?e9ktu?P!7d6z{Qvsϭ]#lWT{O.fC5J@{uU1>dfl6ѧFl`94`h]6@QZDQDP$Kso{ᆪ敦S߱0% "AD^ʋmFxXz)(ǷvazJo%ߔ|<4!;,g@QU %|^eM$հxb'ii+UU FN Ճ"hC$[s$$)c"5)}pv\Owq<ۮi"zO<MZ5|eg5HZλ]Zf/}UvFkKU0%ChxxΡSȎ'72~MJ/7XtImhG5} yܓQ]-|iT\yo1=g xZ= ,K&8jU^1Qa_i֯יLk 5mC 4n?5M!'j*J4b}Ri{mMfUczww.!)ix?C-%g7e~Fٓ F;Tors9$njM&u;eM>xxVK`N 4l(ۮknكg.)hbo*sFhӝ965!tJGrp My3DVU)rM;3Q*QmHJ3jaKGoެ~S5JM ڋ'c5X|]l gn,s8>ٟJSᆭיn1pcJo+E< [4gѲZO/xc'|W}0+iڕ!)aGRS}1Zά"%#+߿:1<-r_9AkSZ68"FFa\Nd/ӣ/[LiG˪Vj(ǖ A)u6{$y'Byd67Cw DB[_2^kuJ=)yp ]/NӑzV"hCta~COhH%Vi`a]M-ml7UΏGx+ZUJ-Hc}擎iڵ Dnf-ӆ΅_woos| L(| uYÉD׋ `-d!ԟ97{OwO:mi؃5+NVosOjʮ3_WJ?7ٯ]iZ5m)tM2s63K6j3zH=Yu'9JR1S:=ZǞOa^F",_CSMJj*3~*ounc,%5k;JǾ`3Ft(Dg4x oN*q@3+ҷ z骯ܲ?Rj.K]Ije U5éTdtko Ե|~eWf7{)u .׌KguoFk07wm9$tӗy&9rǔ~s7c_9$?3o_T~ mis8z%7 To!8'GQ|/֕zҷqd~$`!""@C")}@*pC1pOqDjp!Jq>ڜS+rN1G.0P8h?ratRr1*xQG.9g>O~UUUUUUUUUUUUUUUUUUUUUUUUUUUUU;;۩I8nAu6y۩I8nAu69nۧ7M8[t9ۯ7]xtG nwvyrۯ7]xtG nwv嶣mFyۧ7M8[w-j+tz8z5t.CB^W[\׮zк  ]:c]ڻwvwju략vwjiz]ښi^Wnvvmyq9ǚ흳4q mۧ98sUtc=( ۼ9sݶ6mmTmn8UW]0mn8sUtD mysj8[mSU;mnUYmt鞑mj;rQmUPmNڍmUAU;j[G6Qs5f5E t:g440*6Wns9ֹ֍6ƭ]d8Ew9]clvxmm)(mSTRQQk1(l鞴2BmEv8sZ4v`TQyܨSMjimmEuQGY&^EG(Qg,#8U_"G=ֵZֺtӧOߟ~;CA= * FTT>p$SzUl*dQQx+ /0z$.^dT 蠢x+O2qu"1CBQrGC:LxtpwBxI 6֍mې<AW7ʵUҮz=EG|JUUwÞG:UUUw˥GJ UҮyxy***qWwJUң˥\HPYYY4I#6DRQ^UK^UJD&fTJDyyTG/DyyTI"F*lfKs7,fjq&5,2̗3srnY˙o2Nf5,2̗3srnY˙s&LK9̳%ܹo3srs9̓K8̳%ܹo3sr~|s zgY[nWy]啹&=sr啹]wt$nj+sr7+WY&:%x+sr7+Ws^2ܮr9$njW7+ܮrÙ^rLy%x+sr7+oo_v$$ؒa@If@Id`If@IҒ%U$QUI URC*!TH&!lqmݶC!mj!ln!mj!lKlKl!ltn!!mj!lKlKlHeU$2UI~q/<1w;6I$KW)$1~BIMwI'Zu$.MgI7$U&I$;$/;$ &rI ]$I&k8$OY:[QTBdFd!ʌUu*l!2$tRh3f垹qxftAt̗ 8ySHAt(CD*:G1DA:)DBCDySDHAt(CD*:G1DAXr%૬2@0Vd`Ŕ*@4XuRF@H &FbQP૮2@0X0@* %]ё)TTh* 8J$WQ!d`d`ƊU ,J)# $h)B/*/*[;bX8ḫd(fe%es!Gݮz=o{xYW{uY oZc'֤ɽjɛǽxY.ѯ.uǽxY.ѯu˽xY.ѯ.uǽxY?r?<}cf@I $*&dw$$0]luvI 'ZwԒuvI 'n(u8d>[n҇7vh-緫yku$*"*HUU$2UI ys{1c1c1xk/lb'ܒjI-2MtndtD$]]C8JymJwCxҡ㻷]*;xҡ㵙f9Η N/:sX!ysws@x%fW2+ mBxs gt㌃k3/(9e1Aysw;w4̨ `UHA , qE@H>;y黚Po_.nbī2@0_5>D$sF*vyTO[;bX9Fes!G3)w{a}7jo⯗u|_7ߗ˖f.a=owýQy;cxprt 6RH`?$oI RI֊]CIwӛ F@|nҀswk޽]4{yҀyx{H n zV.(  ˆ ˮJo.\70P>oB{ߩK2XϚ+!@ 6ed({{{4{;!G{4uZ=]{ߏ۰Ϸf(HW;K'I{$.C! T!H<$ :ȝ+*U ,ʺI# $SyU'bs.2fW2q2B-仟9Mx1wwqrױvqwk"I˨n*'=/04uޜ4g=Kn9Pyw4NA$U qOpX~0=H xuB+  9y]Ϝ71c77ƒ)O% w]*BT|1W7X]*qW)~2IԳ2̗3srnY˙z$yzfYnn\7s>Hfc=%|ܮrr&Je\yW=UO3m8s2 !lOϛ;d ykVC!mj!lkTuRsY+ljUI #u1c8t0|I&I7$RHbY$OI.^>:ݻBݮh}{rL8$PC#W ^\>@w71 ]D}fp B\\Uƕs 3+ 8ՙI esk4J{{;d(>c佟}Ӯ1u:k"xsВZI!d7C%@^iAy@^_s %n ކBGR$byy}KCL a >a{;H22m캟9Mx1c1c1c1cww P砓x\q!S JdFd$.!M35,fKs7,ftW3Y^ennWy]vyy|Ԑ !UTʪHeU$2y{9x1 Id^9 =˧$BUt(*d!2'r qEU@H 2B {ǞUn瘜%̅f̤{2y.S{1Gwpp 9v =G @t(G`bBHz*{ՐpIC wu\MkX1c1`s:k\qӿwk;t\ӧ>]:tӧNJ;D2O f2wZȸo֪B89[91o?^{k3Zk^" IdP$ $0$ $OG(qaN'LK:Ynn\^H$ "y:h$1T% H\UGs\>R癛l+3CBd5mնAT<Gsc5.Q$ϑ$t{u z^ak ιsg=񌂫LEU@H 2B {Ü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$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$I$I$I$I$I$I$I$I$I$I$I$I$I$I2|$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$dI$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$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$I$III$I$I$I$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$I$I$I$'ˏ:̒I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I2I2I$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$N1D@A ?pQM>_`C}ZiۧMkt78}B%@ py0o:Db/?ࠟPESPPg?(Y" "& *)&(R b$""i"B&)i((`&ji" (H*"" "iI( "JiZ h"f e $7 淆Lkh`Аgƀ6dmY)ڂmcc:`Í1kZڈMT541PDEDVhfBT,IL2j΍0F`MY VH(&`֭ckFub;A#TcgFB,FCqM4mZɍFAΧ;F#9D~z1m "M [DDmXS9f%$KփFU%bsj"h4b(΍. f+eggU %6b&%SV6#) h&I1FC[mlgaQP(-z}V+# E=B@$#Ā/޿~K"qaΙG/Pm@H$6@@0ܪr @|[C44 /< )F'ݗ Jw~Xtb$  ϨU ox?#?{~CHQCF p5Cb5Ci"FR CTRS)E-%PR"U(*d"b6 [ rCEF.֓TӈҚ `120l`b$ &FfR O|&j*&&"J*H J̑B4HLC1T0M,DK0E%AQ$$2DBLI1U@2 PT$ 2 @LJE,P21L2UDP4ȈEZ|܌fsvv&"SusܲAiLJYB: ҙng;t&ЌS LZ C$"-#rmOk-V?5 $gS`)N"&"&&Th 84PP4,,QI3RRQBDR~ VF1ZZ 8j$",I-l2l I`aD@j XR`" hh ?)Bn{C? rl5W?oq~@;tgs[[$(gk3vm;"GY{sǻk:ޯfM'ͽZomuy1i^*Ffr1#$ID@ RMCQ MM4 TQK7fXؠQZs-  mQS%5@PkCDS2TS@PDAHi(4#& g4AEU- R/ a hQہ Œ@hDFIyO)ZNg&d%` [iB)幗AhC#BTEM!%02S0&Yin2:mvƼW*l?w2Y\,&^u|l֚8usO\LG1-P27O%w[xNg=v޸x}>bPh$l|yh [I ('i~S*!cb*4hj4aZXmm5SH^KYhmA$PF*&HirP!+Ih2@RLDi1+H#VWʪ"tғSgUSJI T (ef$!( `I[BlFX jZɪmml!0D-,EU!l հjFP@@H O\H  -Ӎދ_uwF?K|ggUzpppLszr4&_Oz@~3~{{Zַ{Tzt0B BBt!B))yWŏ/,4t%_𝈰]Gy^ji'~C" *~>Qb() (d!&?Y"֢h)2iICoxCB4*G *H&&)H)(B"!J%.v!obe _wT:UWny>* O'>Ř}e.EYvqF!ݘi.B+W߭t[C1vh$ ݼ.c%EAAAQD`"B_xBQPRU%R%3- SQAE& IbI[մIEkDCM^ΐ.\Q̐ foa: t3߿,Wc{ii_SY~޶ yٮ}& YjM}`b.UFitN^|Y^p. 1U )~..пu)qUnz1|L>_QFϿk9GO-_*x?+1)lYb\'M~&Zy3 GtzbtqU*9?ʕ5snDv?`J䊂c͍a6,ܢE+⎜oϛ"p>U^ޗ*yeLQw稸;?:?J?ߵ[ ( ( ߼zu>%#b-ȱظ%+wZ/ʸ#ve!RNw?Ծ1[Hiwq hACGT!rTW;ѕzE%'N;X JYJiJsO<\% \foEʆ?v+ds /l*[IߑG_Jix \|=wAdȳZCX>LyANdar1p$G;={ K^wSXuwM_j9Ю ,._ΘLaN+/®pjg*8M0[3g<\j%;VА^S0`02"3  E,g|:*}3^@9  ᆐ#KߟY?-(vjil1pOJeO}ߡ`Ds [H#zj_Gev"S{Bi1 EOxz}.odS :{P'ܷ뻞4gpBCXd$ PF!=VJ.+xxCJskZ' YY W'$ 6X&%xڈU =„7ܷ]^Ǚ_WMx{̾>~x:kEp@9( \<^L@E>C 0}FQz ¡B0oIN+H1@KҜb@HWBn@>$┖<ӃE3pVLV^VuqsM\@߿W{Tpyfu>l* 5?mo󖻻"FqQğ{uop35ެk`;di{X~H@(f߅램 w} w=cz #Tس*}D\ԕU3XRhAPWk޼R:w#c^4iz|T?Y@;uH?[NAbA>/L>SDw@O`AyԳ;Ӝ;% 5F4}mZ$際L;b޵hO< L:M?(k1?|6( v~Js#IoaIj)O[ȚY. J@A,bhC8L:o/~n5kR03Դ+R sMk!Ф 0E%քcJU`]Ҵ|E}fy'kq:dCsV=ue괽a%M\'u}zͪ"EM>}<HF8\E= tGq|eu<-6#WP,(I¼V^utĬRf(I|&a)xd*8NoZDI24.*D rt A02$iG K6d L":@`>~pPɕF+V(~]NtI.UTꂦXSvجDT9h: :,kkΫǣjC[b r|D!vޯ`h?)m :x1L?\P&6 :`.XZ1~ʢ(|FBz OGd{F] sd> B}l#a${b)$\7*ީVVj_*}BPɷ@@p\8 s΁(_Smda43-1~ <~mt q57fg-~42Yy䇕KF0(˿m!!B5~&' ‡"jo yY&;>VP!R;kX|"CA嶌JO"]Q47$1K,-"Q6-jY0H # % R}A} @b]ii M&I^"~}kQ{z-Gm:xȯO4 #3*|DBb ٢ '*섲 ^VQ,arb^ $eE\2'1Rt{{?'گA{@ `<5߫p?Xh-m JxƘ1o@ʺ0`OiNkve^U^0a=+O&_<{'JT@ Wć6y z=Q#iM%(3F/r%Ң_Vii8~?S Qr tHov.=3..@>E#Xp^$yn!hOg!fWV!q1ο*MOȰөV ItE3$F3E9X'&XUӍV8O]3KQ_zc/>4- !L }=hϻ>ͩ߭wgmM߮2{B4)chy=mj01X{!iE۸R֗\R-GsG__'!ܖsQ1 jf"O/N^f$U#wwJU#@P2㗟W>|`0BC1TU&2wKq< bo'PrSRmHbbw Ed.̈́r˘'YE_>ތZ< W&ȈdT~כDc?|-ϯ0rZ[{uNBlsެ}%W=V׭)[{͉-=r|w xv0QSx.[_L^{_`Ցs4u}uF'G,G#f!p.~zɤ wnVB2Sٵxo(ABA@&'N0Hx$不Ӊa9,݌4Ds3 O9sx6/ @ِ 9Io#j ate Iv !h/h/I39RV[ҷ67uNU-_$npb lJDº_Hxc|xxĭAɮ@Y:Fȁ͖L,)MZ{uI_[jD!#~%ahŲӋHs7' \cuF#+Ɨ` Ttb{`G*s<;tMHC;dqb8~B?Z~H~ B&JޏTMbN7Vʹw&F/J;HY(EEBQB?q=Jz@i Yn& V +,ͫL=D| SUh$K~ߥA'Fs?ii ԱBI;3ID0vGx:H[o2~DE-qGD Bks0%!L]s`jOH(f|~"5E_?/:*}v'܋13tA,s _h~ԥe;?iJLY(pڤ*Y-wZݮ*KެiW:&I2-yq|@C`,j~#51HA8Wjq`t,0\D8n d^6oO='X7;1A^TvaY xFo+ *HK:س;Q}|)+ /i2M}%:ZӰ"Wb/YEj+9 YfaTzKQ(]IDG)˅mwXf %*}NkҦz\~ar{e~H9VыZA+@A#a\#H!Ql(uˀ X\}Eˠ h-;81wjGb#6Tjb9dfr o}6cժz$9҈u={4ƚ빓kx֦),RFYIrJK|,TcYFR QYX] uVeڗ(ZuDg ,bYq<A~/:COgA) I (r qR=8 DEwU\?)Fs?T~Mc& g9.R1pp})P⦆vOm]P|CE8m0tەnRsjQNyU u^ªJy;;UKSR4Q,S|%ٌ+Sٚ|dN)#f|1I’ckҐ\HOyg$tޭyUyyS&~Cc4$+M "HUsa@=~'?~dS ^g{n0 h϶ѤCP _qCxg /brz;)Iz`Hx2(3;Mi [tȈ# 3F" *j#纤5f^,,Z((ʠ$КbHzUDDCH@дhNF56dd>0OCGv>N8;eY$A"@JȊ2*W}QR%Q3WnhJnp|}C?qѢ‡bx^f`{n6@?|) r$%&W$(wѪ#QAҮZTм I)|~H>^'w a;c@Fx;k<2 M>Y\¾8Ofb]!ݪdTLo/F d %(R$KsIĮHRPRWz)C1.  1x|)5LDD = & $<@diN2Q>?H:X{?g80zswpD S{J4_jQ<{F璒Xu9d#:njaTM4 B D,QEe<(k#A1yUn)-dJp,DGT I"җ2|r` bÀ3OtM>P"a2B>J8=΋v}=|o(umk󟟝ML X+'77puƢ=ra\<6,2đCɤ%)fCl)eЋYR(∐(ǫ!Y&E&7}Uf2TyD,9AnBA M'-Џ= &);i;RP /L)GfHV_Ԫ}a>Aτ߱Ҙ_BߛkvP\+G_\Jlǁ#HaDDEeJai3A6Q ~"duͺɾ\c@^vh3Aa0='G*ғ8} <AC:$#w4WRYq@(?>lKdX0EUN|K&r-tя &ە |hӟ]wHȭ> &3J={֒lGلA( " I$(>&""`GO =dq|DlBE2GQ 豈F:}X}?M+3uOp3\IlYꯣ(bdt*TᡂW¥Y½Mf-)HPy*1 P%OӘ-#J|$x\|P8jPC>nDܪ0qE>xd>lJWσ#LP OkWff`*RݕB|H H #΅(QY\sG(^lz4% TP9tx!b4jq؇9L=Ic(EzoHt4B aYDc.w  D dR)H6h&@dt/D{/}^3:L&e]twj '2 k$ #'Sd<@2{zGb1̓'!} '+D #(#"$ HcMY7'j^@{ שe\b i Sm\pQpu@|^=]%Q"+E}Nm/(uzR"(A>7xXU}_;K.yw#g-ag; ;!$R A|Ghy|E}1h{P KKA#8!y*!Rb)/Wp"hS(dq@ɁE|Eu~a:~x9 yw@R!.R:$f \ SHps<.  !ĹmNZh%‰Rp}֨  &]~gҥf2. mso gN0\{#nYHIb \Htd}"_c|#>1G8)r/ U1QD}Ij":Uul:&Gf%ׯ^}UUU7{/.|廹Fڊ:x㣷yimUPmNڊ6j6m(>7۷L`ֵ`ֵ`ֵ`ֵ`ֵ`ֵ`Ҕ R R R R R V [l[l[l[lPCIףp8s9q`0c1 6clޤT5 4P0LECPI1 C $T5 4P0LECP3333331UUU_k+}`}91T;j&scm9vLsP1"Q3ciECg86ӘmDqm1k8yf5kYF0kZ0iJR)HAss"G9Ü`0m{ ECPÍP*Nb* v۲۷$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$u]I$I$I$I$I$I$I$LLI$I$I$I$I$I$I$uRI$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$I2/{?ӧKrBG@N)HB[d*%D"AE`Zb~ZPCIC`"a r}LEH$,, @ :T"B/>o91ș"ap\rb*D!ae~8ϣτI$I$I$I$I:뮤I$I$I$I$I:뮤I$I$W؀MvkZֵk^$A$HI$$8q$I$I$I<333 330>I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$8ffI$I$I$O333 330 $I$I$I333 330>uczP0i$ێyB1 0>_/>G $I$I$I$I$I$I$I$I$I$I$I$I$I$I$}y$I$I$I>333 3308<$I$I$Iᙙfffffa|>I$I$I$fffffa{yy$I$I$I<333 330<<$I$I$I'ffavڻv۷j۷nݫ}׫^ʪ̞~圹qwטgyͶUPchB2 H?~vMDH"QhZ,իVX/iAS0\ .zLP, ,S30D& i Ǐ`ќ汶؊"D2& ˗,!aeΜsf}|$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I8q̒I$I$I$xfffffax<r~|9͵yy:x㣷yimלmYڨьe!H@CQ P% :q6҈DH0t۷nnNya9Ms"N-^o}~|? I$I$I$uRI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$uRI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$uRI$I$I$ddI$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]u$I$I$I$I$I$I$I$I$I$I$IO>>8Z5272BG:t )JBTk5)!Q*%DZ-~<cN<ș\.YEw$TEH$,,UJU*V0"DH,|Q3cl86"dL. 9$Kˇ9p~yGI$I$I$N$I$I$I$I$I$I$I$I$I$I$I8q̒I$I$I$3303338yI$I$I$330333 9yI$I$I$333 330<뮺I$I$N$I$I$뮺I$I$N$I$I$뮺I$I:>96fN$I$I'fff۷j۷nݫnݻvl yu]u]u]uXI'BI!$Hx3?|ϣ$I$I$I']uԒI$I$I$I$I$I$I$I$I$I$I$8ffI$I$I$O333 330~~tm 00J=ӧN:dI$I$uRI$I$I$I$I$I$I$I$I$I$I$8$I$I$IFffafffyI$I$I$O 330333s<I$I$I$u]I$I$I$I$I$I$I$I$I$I$I$OÎffdI$I$I$fffffa7Y7Ạt<9&o##I:s?~9UUUUUry>|Iϛsӽ8s랝1]#{mH1t87uçwyۻmn8m;gjiޑ435S4DELC3Q3DM DT435S4DELC3Q3:ooS~Όc(g3QbLs;j1VآgmF1Q31[b&s9+lQDg3m(|ۍk5< k3Y8YݝZ kZ kZkH H14"BLM'p8}0c`Bs5S;l# &f*gma`s5S9c=vv^yJRe$8;xg81 6(99+lQDg3sMۛq k9g,3qٙWkY5?fqW}>Kߟ6wͶvQAACB  )k4)!R`֔ [lDDH"}hx  cs:82&D' !DJN'$OwU=??:1Vآaà cqmdL!11$"tӤI$I$I$I$I$I$N$I$I$I$I$I$I$I?332I$I$I$~333 330~~tcE>o;}~_̒I$I$I$afffffaqǞyI$I$I$330333 <$I$I$I'ffafffǞyI$I$I$330333 8ֵ{{̪ʪ̪o37{{ݙUUUYUUUUUUUYͿ_Eo7|'?a>AM~jREE6{j**)UUUUUUUUUU@߃?ׯ^zׯ^~/3*23*23*23=Zʪ{{fUUUUfeUUUVfUUUUfeUUUVfUUUUfgv@_/j= زY} YVۼnݤ3VUUUUUUTIjYf͛6lٳf͛6qYeYeYeY`V@J23*37{{2UUUY o{{eUUUVfUUUUfeUUUVfUUUUfeUUUVfUUUUfeUUU_|_~u||{7M8[w-j+׮z۽ݩmUAU;j(mSTRQh"Hk {qNbbbbo|^zj6HDBB"b81\c18gL1PPdzϗ1ؠ6@)Y.Z`jJT(⪄Uܸy<^Gqjr\qq5LfL۷^;v$׏.\rk\qZ\rUy^t֯.3j׏.8=/;fS ;scs vn^q9kYUyO}~$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$ddI$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\}Gׯ^m vtRsӨNc]LECP_K|^~~tL|.||syI$I$I$I$I$I$I$I$I$I$I$뮺I$I$I$}qffI$I$I$OffafffyI$I$I$O 330333s<YkZ}kYs^feDBXr v^8DDu=;Ғb$NXOOOZzyѶg8'1yoׯ^I$I$I$I$I$I$I$I$I$I$I]u$I$I$I$332I$I$I$}ffafffyI$I$I$O 330333s<u:~>􉡙uOonyC 03 SGI$I$I$I$I$I$I$I$I$I$I$I$I$I$Iy$I$I$I>333 3308<$I$I$IᙙfffffaqǞyI$I$I$330333 <$I$I$Iᙙfffffayy$I$I$I<333 n۷nݻWǎ:Ǐ_4; kZֵkZֵg9QȅŜ\:smT'mE41'|^DM`d{a)LG#@HH3>I$I$I$I$I$I$I$I$I$I$I']uԒI$I$I$8$I$I$IffafffyI$I$I$O 330333s<%"$I\O/Դѭ8 U:4&!0B4ABBhfy-ʝ(hZ;_¨ (п@ iJPP,CI-֝ Ji)Bմ/UO@>뾯wνw}tuff:tӧN:tӧOʪ̪ʪf~ۍ{{feUUUVfUUUUfeUUUVfUUUUfeUUUVfUUUU o37{{ݙUUUYUUUU,,,]B+%` 4`ѣF4hћ@)ݮ|zzz====>3*23*23*3{{{{eUUUVfUUUUfeUUUVfUUUUfg:23*9r;??39ffffffffffs2n8t8:uJۆ9 &&jl롃{7Y;Mz맃7]p:[=sטgzl9s9ͶڳvDL st9ÜsjQQys9smՌc1e2r@....8aP aPocdpX0̀m33fp* ;L0EKS3;lg bL۷eNݸyQkG2BH bqx6PpCs9cmE9pv8g|fI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$9ÎI&I$I$I$Offafff*qÎ|>I$I$I$| 3303338yI$I$I$330333 8?:!F0`4hѣB  k4)kZ )JBzB`8pp`g8qIǃ`d!`f"H$e$!`f"f"P*&ffeP2q*~~t`ڈ3PV6PDPHHd0Ç0g91ĆBa&b&|9GI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$SLI$I$I$afffffa;[{y{{̟˯{7Yu/%=7C,TEK!AMTTđ)P 2!!!==330333 9yI$I$xfffffa{8|$I$I$uSfffffa{yI$I$I:뮧ffafff>|<뮺뮺뮺뮺I<$Ig3s9g39vs923=~~s}׷=-Εy0\8pÇ"8pUUUUUUUUUUUUUUT:tӧN:tӧNUUUYUUUUUUUYUUUUUUUYUUUU{{ʪ̪ʪ̪ʪ̪ʪ>νzׯ^zרzWߺׯ^zׯ^fUUUUfeUUUVfUUUUfeUUUVfUUUUfeUUUVfUUUUfeUUUVfUUUUfgo{{eUUUVfUUUUfeUUUVfUUUUfg/g_<-HW ۷xnݻv۷nݺ}_vׯ^zׯ^~'3*23*2UUUYUUUUUUUYUUUUUUUYUUUUUUUYUUUUUUUYUUUUUUUYD7#'ӐUUuFKZM!ERim09'T$R:%D:\Mij`bi[\9PДPQ2TwxG0$h4H! Bi4iQĆ΍ɜ(% Ef P_?Jtv Rʨ3 RޚYᓔP$-Sjǵ=l~ $߳s^`zCգȵkĐa?z,)`/>>OVpyvmJ<-ۓurSlVRL{!m%͵stnr^-}63/zlbb_ݕ , " *?3AE NqӦ?9 t%!CPE N%%2EM',LjfZh"(((` F3t)@D,DTM2PД": A@t[c?kpM#B#Zc21\ci,l!$1PAꌴI dh98ґS1L !G3& y h)6ג. E.! v)]˵w'oo[%tFp@ohߪ( lFCV[" \:]^V2IZ4>U,WzrU p~oU/DQUUi#ikE8Π%h5N*1&$h(hhjf&hi6m6U3F΂ `("bBa( ) :w;/?I~Š " (JB=NLA]Sr$F( #;pJFbhjRF(XJe'MG0y0! ډ)*` M$QQ3fMAM4U-3S%SAs6[Ƃ)0^Ȓ@$ DJKJ?!*+= 쾷Oَdҳxa?wƞ}~_M̀@X;QnmZm^r{8~ʾ؟Ns@΄ADMΚ)bRJ(Z)FBJJZwDI*hMS @@4R᳘ &)a @@ESĐ 'FGw}6ŻЮ']X5!o<*ʰ'DT s~~XSg#'Voq]a0pH$ ee*@~Uߘ H"*b h)0"*&~c?&@)H4,J )2OgճARDTQBBQ  S귻v}7i!-tF f42ngH٠^lf|G:svn󮛱Wz;De[ 1wݚ(Oͩ@DS@TK"a*)JFPP~;@ "("b d ?h!"iH(HVbit&>Ш/|]"8u%mr3VAK;Z qok?j,6'lcxxo:UBG{±oNAe^Q]wk#|" 6D!@ -QӤöMD) B(CH4L|scB4 BQ(H"( >d?Z紇ߺ,m_۱t{7S>1.ͫ`p>ql[qUp_e`A>$ PBɡFJb41mŪ:i1 D . kE @PLĔCBMVBBQAD0PTSt7ۼ$2T%k%h)5(lvASP P!@PPPE4$IuBz[ )QS7}=\e(Dwp¤qʛk\35%ԃj׻L/Euf/tseLt]=Ζ]ٖbH?z%@T?$gd kkl4XhF1ZXα&(#05"(%&R "$I"hQHbi"&)%~/>/v %BJh +@&iҙh3ikFڌd`1BL J -AR3G  e9hB &DH Ժ6R`:Tˋj3jh{v9ڟL^{٬?}cVk1VQ;oOtO}-vǻ f͌[ⰖkGAye{1!EqAEL2PDLGjti((hijbCF$-M41 D@QTPQ,S3SH4L" ib &"a4SG}vi{cղl&)-&&JKyi ! 1, %\5 ,$ CAD4DQLL)ʢ(z~S]oeB BC/ @(@קMՋwsIJp;Md\y:?T_|?{8YvWҟSvX 5JL'ý6)I<{3e೩$ ]J  (&hOv$$ 0l%lIQA3$P4R TL5ĔΩJ ($( ijP4)( ?c.p GnlMi9(*1Ԅ%ÃHH:KP@M )Q+ ԔFw Aڢd61UM1D@ihPE8tH+^A:V}3K5.O夈WT\ykzR^.!WwxtS[eI{>NLS شn׀~j^l~V9ʁ?.bCQ8ݎ`咼A@B!@ b# J&d&!i_Q'HѤ4(1!B$0KC5ɢ`O%Q> >icfǭl]rV*doS]ԃn|p<5j%bc]u1] V*MՇEVqj9Lfw:_nCxQfQ٬e.77\BHXl +UƞHIVDTW?[LA~&a "!"mEA3LMS ; \~+.4^̟8DQ>T?|~VQEBۿUBAoC(B@{Ҁ R-b@H~H _\_߅ﲈ.wh  *>Ϊ{x(T-}V2RLImB1! @(& ~"f)J$(!ajϏQJZ ",bTO_~K WAbp\ s^!yTt>lu v []d[ظv ;kzH('wJw|2K)} ]&%_),S ɣ9XP~# C}3EQ_uƊ(((((()UjUdXw$S -3shazam/R/Core.R0000644000176200001440000000245713752621627012774 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/RegionsExtend.R0000644000176200001440000007371214071345330014651 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(oneseq_db, package="alakazam") # germline_db <- list( # "IGHV3-11*05"="CAGGTGCAGCTGGTGGAGTCTGGGGGA...GGCTTGGTCAAGCCTGGAGGGTCCCTGAGACTCTCCTGTGCAGCCTCTGGATTCACCTTC............AGTGACTACTACATGAGCTGGATCCGCCAGGCTCCAGGGAAGGGGCTGGAGTGGGTTTCATACATTAGTAGTAGT......AGTAGTTACACAAACTACGCAGACTCTGTGAAG...GGCCGATTCACCATCTCCAGAGACAACGCCAAGAACTCACTGTATCTGCAAATGAACAGCCTGAGAGCCGAGGACACGGCCGTGTATTACTGTGCGAGAGA", # "IGHD3-10*01"="GTATTACTATGGTTCGGGGAGTTATTATAAC", # "IGHJ5*02"="ACAACTGGTTCGACCCCTGGGGCCAGGGAACCCTGGTCACCGTCTCCTCAG" # ) # pja <- shazam:::plotJunctionAlignment(oneseq_db, 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", size = 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/RegionDefinitions.R0000644000176200001440000002175714067117155015523 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.R0000644000176200001440000004121413752621627013660 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.R0000644000176200001440000002535114067121740013314 0ustar liggesusers# 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 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 #' @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 kedd h.ucv #' @importFrom KernSmooth bkde #' @importFrom lazyeval interp #' @importFrom MASS fitdistr #' @importFrom progress progress_bar #' @importFrom rlang sym syms #' @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 #' @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 #' @importFrom tidyselect all_of NULL # Package loading actions .onAttach <- function(libname, pkgname) { msg <- paste("As of v1.0.0 the AIRR Rearrangement schema is now the default file format.", "A description of the standard is available at https://docs.airr-community.org.", "The legacy Change-O format is supported through arguments to each function", "that allow the input column names to be explicitly defined.", sep="\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 # 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.R0000644000176200001440000026374614053000607014626 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. #' Only applicable to single-cell data. #' Ignored if \code{cellIdColumn=NULL}. Valid loci values #' are "IGH", "IGI", "IGK", "IGL", "TRA", "TRB", #' "TRD", and "TRG". #' @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. #' #' @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", 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 single-cell input if (!is.null(cellIdColumn)) { check <- checkColumns(db, c(cellIdColumn, locusColumn)) 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)) # Single-cell mode? if (!is.null(cellIdColumn) & !is.null(locusColumn)) { singleCell <- TRUE # check locus column valid_loci <- c("IGH", "IGI", "IGK", "IGL", "TRA", "TRB", "TRD", "TRG") check <- !all(unique(db[[locusColumn]]) %in% valid_loci) if (check) { stop("The locus column contains invalid loci annotations.") } } else { singleCell <- FALSE } # Disallow multiple heavy chains per cell 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 # heavy 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]]) # 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 <- groupGenes(db, v_call=vCallColumn, j_call=jCallColumn, junc_len=NULL, cell_id=cellIdColumn, locus=locusColumn, only_heavy=onlyHeavy, first=first) # 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 <- groupGenes(db, v_call=vCallColumn, j_call=jCallColumn, junc_len=junc_len, cell_id=cellIdColumn, locus=locusColumn, only_heavy=onlyHeavy, first=first) group_cols <- c("vj_group") } # groups to use if (!is.null(fields)) { group_cols <- append(group_cols,fields) } # 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 new column for distance to nearest neighbor db$TMP_DIST_NEAREST <- rep(NA, nrow(db)) db$ROW_ID <- 1:nrow(db) # 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) } else { stop('Nproc must be positive.') } # Export groups to the clusters if (nproc > 1) { 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") 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)) } 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) db <- db[order(db$ROW_ID), ] # Stop the cluster if (nproc > 1) { parallel::stopCluster(cluster) } 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", "ROW_ID", "V1", "J1","TMP_DIST_NEAREST"))] return(db) } #### Distance Threshold Detection #### #' Find distance threshold #' #' \code{findThreshold} automtically 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 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", #' jCallColumn="j_call", model="ham", normalize="len", nproc=1) #' #' # Find threshold using the "gmm" method with optimal threshold #' output <- findThreshold(db$dist_nearest, method="gmm", model="gamma-gamma", cutoff="opt") #' plot(output, binwidth=0.02, title=paste0(output@model, " loglk=", output@loglk)) #' print(output) #' #' # 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) #' #' # Find threshold using the "density" method and plot the results #' output <- findThreshold(db$dist_nearest, method="density") #' plot(output) #' 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)] # Gaussian distribution bandwidth scale parameter # gaussian_scaling <- (1/(4 * pi))^(1/10) # Ideal bandwidth bandwidth <- kedd::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_string(x="x")) + baseTheme() + xlab("Distance") + ylab("Density") + geom_histogram(aes_string(y="..density.."), binwidth=binwidth, fill="gray40", color="white") + geom_line(data=fit1, aes_string(x="x", y="y"), color="darkslateblue", size=size) + geom_line(data=fit2, aes_string(x="x", y="y"), color="darkslateblue", size=size) + geom_vline(xintercept=data@threshold, color="firebrick", linetype="longdash", size=size) # Add cross histogram if (!is.null(cross)) { cdf <- data.frame(x=cross[is.finite(cross)]) p <- p + geom_histogram(data=cdf, aes_q(x=~x, y=~-(..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_string(x="x")) + baseTheme() + xlab("Distance") + ylab("Density") + geom_histogram(aes_string(y="..density.."), binwidth=binwidth, fill="gray40", color="white") + geom_line(data=ddf, aes_string(x="x", y="y"), color="darkslateblue", size=size) + geom_vline(xintercept=data@threshold, color="firebrick", linetype="longdash", size=size) # Add cross histogram if (!is.null(cross)) { cdf <- data.frame(x=cross[is.finite(cross)]) p <- p + geom_histogram(data=cdf, aes_q(x=~x, y=~-(..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.R0000644000176200001440000036437314063420465015175 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") #' #' # 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=100, 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 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") #' 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") #' #' # 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") #' #' # 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 one isotype and sample as a demo #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA" & sample_id == "-1h") #' #' # 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") #' #' # 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_string(xmin="rect_min", xmax="rect_max", ymin="ymin", ymax="ymax", fill="text_label", color="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_string(x="text_x", y="text_y", label="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_string(x="text_x", y="text_y", label="text_label"), color="black", hjust=0.5, vjust=0.5, size=2*size) + geom_text(data=sub_text[[2]], mapping=aes_string(x="text_x", y="text_y", label="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_string(x="text_x", y="text_y", label="text_label"), color="black", hjust=0.5, vjust=0.5, size=2*size) + geom_text(data=sub_text[[5]], mapping=aes_string(x="text_x", y="text_y", label="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_string(x="x", xend="x", yend="score", color="motif"), y=score_offset, size=0.75*size) + 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_string(x="x", y="score", fill="motif", color="motif"), stat="identity", position="identity", 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 sample as demos #' data(ExampleDb, package="alakazam") #' db <- subset(ExampleDb, c_call == "IGHA") #' #' 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(5, 100)) #' #' # 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) 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.R0000644000176200001440000054043414071345330015545 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 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) } # 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) } 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) #' #' @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. #' #' @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) #' #' @export slideWindowDb <- function(db, sequenceColumn="sequence_alignment", germlineColumn="germline_alignment_d_mask", mutThresh, windowSize){ db_filter <- sapply(1:nrow(db), function(i) { slideWindowSeq(inputSeq = db[i, sequenceColumn], germlineSeq = db[i, germlineColumn], mutThresh = mutThresh, windowSize = windowSize)}) return(db_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}. #' #' @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}. #' #' @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){ # check preconditions stopifnot(!is.null(db)) stopifnot(min(mutThreshRange) >= 1 & max(mutThreshRange) <= max(windowSizeRange) & min(windowSizeRange) >= 2) # 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)) { inputMutList <- sapply(1:nrow(db), function(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 } # apply slideWindow on combinations of windowSize and mutThresh for (size in windowSizeRange) { if (verbose) {cat(paste0("now computing for windowSize = ", size, "\n"))} for (thresh in mutThreshRange) { if (thresh <= size){ if (verbose) {cat(paste0(">>> mutThresh = ", thresh, "\n"))} # 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)) } # store results for each thresh as a column in a logical matrix if (thresh == mutThreshRange[1]) { cur.mtx <- matrix(data=cur.logical, nrow=length(cur.logical)) } else { cur.mtx <- cbind(cur.mtx, cur.logical) } } colnames(cur.mtx) <- as.character(mutThreshRange) # store results for each size (and threshes under that size) as a logical matrix in a list if (size == windowSizeRange[1]) { cur.list <- list(cur.mtx) } else { cur.list <- c(cur.list, list(cur.mtx)) } } names(cur.list) <- as.character(windowSizeRange) 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 sequences (as opposed to #' the number of remaining sequences). 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) #' #' @details For each \code{windowSize}, the numbers of sequences filtered or remaining after applying #' the sliding window approach are plotted on the y-axis against thresholds on the number of #' mutations in a window on the x-axis. #' #' 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 = TRUE, 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){ # invert (!) tuneList if plotting retained sequences ylab.part.2 <- "filtered" if (!plotFiltered) { tuneList <- lapply(tuneList, function(x){!x}) ylab.part.2 <- "remaining"} # 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) plotList <- lapply(tuneList, colSums) if (percentage) {plotList <- lapply(plotList, function(x){x/nrow(tuneList[[1]])})} # 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="Threshold on number of mutations", 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") #' #' # 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) } 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/NEWS.md0000644000176200001440000006370114071620517012645 0ustar liggesusersVersion 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/MD50000644000176200001440000001526614071672543012070 0ustar liggesusers7600638f1611faff7396bd8901b46e79 *DESCRIPTION eea8c543027706ef42c44d7f2ae9c4c1 *NAMESPACE e44a6ef146a6df5f2085e810898e1ba3 *NEWS.md b18f652b239b60c89cd710a1762713dc *R/Baseline.R df12b5bd4073277a19f475fee9739c07 *R/Core.R 5e9ff3a699566b1b94dddd4b910ea7ca *R/DistToNearest.R a18848a34df75011eeca7c26167a2cc0 *R/MutationDefinitions.R 3917624d30ecacb6fa6558113699c47a *R/MutationProfiling.R 361f7757cb0cba262541c706ce2ce9f4 *R/RegionDefinitions.R fc15107719805e91adc25d2798c3756d *R/RegionsExtend.R 9ef955059be0ba4ba913f195e3864e9e *R/Shazam.R 865102bd921dbd0c99a38c8ad734dba8 *R/Shmulate.R eb9b560edfed22d042ebce0c0cff6477 *R/TargetingModels.R 70059491eea793568ceb871bb52b3eb6 *R/sysdata.rda e4ac289a5d4753b7f9ca4462eca14fb0 *README.md 20daf777936531ca60401b4338d935ef *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 633162ebadeb779b6f07cddccfb087ed *inst/doc/Baseline-Vignette.R 8aca38d942ca9395c133b97353e79eca *inst/doc/Baseline-Vignette.Rmd 3831c09ca0a2ac5ae430f05c5966d8a9 *inst/doc/Baseline-Vignette.pdf b84e0fb9784ebd2d26ead84cca75f805 *inst/doc/DistToNearest-Vignette.R cdc995f738b95cac79233505ca34f27e *inst/doc/DistToNearest-Vignette.Rmd fb58d88a8810adf5d1e5f6c44d1f9e06 *inst/doc/DistToNearest-Vignette.pdf cd427fd702054a12859598f8376bf2b0 *inst/doc/Mutation-Vignette.R 9339c5d2d2dd916708b5a66117a254b1 *inst/doc/Mutation-Vignette.Rmd efd5a2aefeb5b40160e6a7abdd2c08d6 *inst/doc/Mutation-Vignette.pdf b791bd26158541e68c33d0f9851a367b *inst/doc/Shmulate-Vignette.R 55515e374ce9c2dbe893e66c99f362d0 *inst/doc/Shmulate-Vignette.Rmd 480dc48868b65391f053f049cd93eef7 *inst/doc/Shmulate-Vignette.pdf cc70c81fd5d75b0877d869e3074ba196 *inst/doc/Targeting-Vignette.R 0c85dd613680efeab93432246124c5fc *inst/doc/Targeting-Vignette.Rmd 87e2e5fdb1d5cc36d78b3d77ae9e7065 *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 830c5c9c1e78673f75a5c297e7c54d11 *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 a10072e80ed50974a97d1f21f091260f *man/collapseClones.Rd 21ad62e80ed159cd868741a625e12323 *man/consensusSequence.Rd 4e0eb86d5e1b4fbf0253f3c292afa783 *man/createBaseline.Rd de72db904913f5cbcb0a1e6cc145043c *man/createMutabilityMatrix.Rd 291078043a96c53414245a7e245510d4 *man/createMutationDefinition.Rd 492ef3e940bf53f731ef9e5cba3a8653 *man/createRegionDefinition.Rd 7d11f1aee10dede25c1d81cb969fcf9e *man/createSubstitutionMatrix.Rd da06534ed4a1ab6af435fc8556b03ac9 *man/createTargetingMatrix.Rd 11fb1e7ae75c208a090b94568b9d583e *man/createTargetingModel.Rd 30636a595b9976ec9ea128334344bc2b *man/distToNearest.Rd 0f10a3bd11b7ef8fa9be4fe384d544d1 *man/editBaseline.Rd fe6d14dde1b8cb3d9a83ad9c57d2ceaf *man/expectedMutations.Rd d7c9d0bcf61ddb5894afb400b84b8a97 *man/extendMutabilityMatrix.Rd ad87d67c4a882136c7afa28101a5b37c *man/extendSubstitutionMatrix.Rd 0e2d0715d13b44812842c8e1db911272 *man/findThreshold.Rd de6352d125ba818a4f31429f1220be1f *man/groupBaseline.Rd a64599327530e7dd2e26c79dc2bf27ae *man/makeAverage1merMut.Rd d3ad96cd8466448271e018d0774ebb44 *man/makeAverage1merSub.Rd a421abd5d1b8546f15e2926a3d3d601d *man/makeDegenerate5merMut.Rd f1e35fb60180aa5226b1ca07fab136c2 *man/makeDegenerate5merSub.Rd 5fcae87eb1a59f104efe578af61271d4 *man/makeGraphDf.Rd 3b3be58d2c5c40ca58c028f8a51e82e6 *man/minNumMutationsTune.Rd 94699556d289cd6dd2667c60b038662b *man/minNumSeqMutationsTune.Rd da8d087656261ace366c8b452411d249 *man/observedMutations.Rd 622c0049dc0cc9dbf4eebeca555c151d *man/plotBaselineDensity.Rd 5e2f52216694d5f67f8e7482e24163e8 *man/plotBaselineSummary.Rd cf65429261fc48ae7b596ea422238d63 *man/plotDensityThreshold.Rd 767b693dd2766bce94522103c3bb3459 *man/plotGmmThreshold.Rd dd51d0355510732690a33bea5186ace5 *man/plotMutability.Rd 17095b8bfb1217af965e15a469bf7888 *man/plotTune.Rd 4d11abe48d8f5e735225cea8bc6de2f4 *man/setRegionBoundaries.Rd e946ccbbd09fe107e00855ce1e8746f5 *man/shazam.Rd ab6c72f099c6fac7cb2c127428f58e16 *man/shmulateSeq.Rd 1b69477030e9204221b91d6e138f2259 *man/shmulateTree.Rd 7ff7bbe22f652c8ea5ed2c6f752a7638 *man/slideWindowDb.Rd 9ee27a125bda5fd7a296641495d9de29 *man/slideWindowSeq.Rd 3644a691b5aece662d8920745f91c298 *man/slideWindowTune.Rd 0a6e9a63813d58f2383d52fe98230a0b *man/slideWindowTunePlot.Rd 820146be6dfcfe9b6f29bac411d48ba4 *man/summarizeBaseline.Rd a347b3e7a51961a499586a701b9921f2 *man/testBaseline.Rd eb562fee7fda5169a426ea340a6a0dc3 *man/writeTargetingDistance.Rd 8aca38d942ca9395c133b97353e79eca *vignettes/Baseline-Vignette.Rmd cdc995f738b95cac79233505ca34f27e *vignettes/DistToNearest-Vignette.Rmd 9339c5d2d2dd916708b5a66117a254b1 *vignettes/Mutation-Vignette.Rmd 55515e374ce9c2dbe893e66c99f362d0 *vignettes/Shmulate-Vignette.Rmd 419916bac0eb8cec6ad6a664476d48f4 *vignettes/Shmulate-Vignette.html 0c85dd613680efeab93432246124c5fc *vignettes/Targeting-Vignette.Rmd shazam/inst/0000755000176200001440000000000014071641077012521 5ustar liggesusersshazam/inst/doc/0000755000176200001440000000000014071641077013266 5ustar liggesusersshazam/inst/doc/Targeting-Vignette.pdf0000644000176200001440000064026714071641077017507 0ustar liggesusers%PDF-1.5 % 9 0 obj << /Length 1687 /Filter /FlateDecode >> stream xXmo6_!t 57X?t], aȶK&I_;eKt tbQwsy xp7?HI|X1 #xcV>5j762|'tYɻ|Y2Wy~? zD 3Q$QO2!`Ә@N4L¯iYE_}A0̕`R)Nr)< *);,F-OɼaÛ.@pfSZ$$}V^9XTt 䡍v"G\wI -M(ƳW,҆wYHpOiJEnٺ"Z3.[v8S !A|jmV؏p%&Ϧg@UYYwV\v&]V-2K/.iLd& ,I8@ _T@.S e*// \m :8 ^kgF,Z=? u<֖$n#Li QxOb}.BWq|K 4v [΋u<8۵y1;Ⱥm%ڢ _2Tؒ rB|8q&㔜FI:6=- hjV=&uݔjgX9ɶZi4%/9iHJީ%wĒ{U+@8ka@{05%jzn-mސ&D>&Bt]=;lg뢨q]|˯ۧzSRM o p\TuN`b){7 ]" $A)\+dALHAUcDIN)cMt5#a40C*IFUEeM}G(=Jj9X ކOuQ7գvc[JH{e E|/u T2S V dxSonC;m{\6eq2wOsvvW^Su)&fZvx<9dS]y݁14`V e4-3oc.GTs|=df?D&dAm*ʐ>ʌz eMY i>`rnv`+88xd7OHˊ fOŮM]]'=@F֞Ƈi6@dI}IK %1›$Z ~5(n1(qX;)T(< =q_QM8⁚@6$\ MYjbuQfgbe3+1A3 G~W?يFzƗ&1.AG{>L&k>Lp&'W6~k~/A(֣3byX&ȝhɇ{>z|&HrKm{7З`c$|: Ӫo[̇:/ ֏բo;dK[ʧI={HcHwP[Ŀh$S> stream xZY~_ؼYC>}nUFaD1FW!=`JٚT0h[b;@g3 $e1pGUVykȝ4L! G*N _\xUmӽYKVٸ!aLÃ2xSǶh wC'JPƴk2mi4 oE IAQ$lFQ$s6Mw0l5.F2A'Z0dUO i$hs/o`>}_L8U|FYF#I%Qjt~G{J*-C!, d8i ͮn (r,x!( E,{fjCm~VQfUEvg SRpI0nsgY%ĵ]:mF]'jvvŏUokuCs!_#~7bm[~/%EY'7so#+P:%5amb/w."mNUA;]#f|1[*!0ٵawsPD 0nl~[&-(Y ^q(4#n^0c@bB-yH+&ޖcgχk>bu)R B'r45s\ 7àqd]S ?}A>_B&a|~Bùγ0eeSнൠ@gAakGრcJ:#RJ}t>:N|8Q>@}e @xRm6ҷO#< #II0lwL`Xյ+:tb < ^8x!*I;|[5U]=p:v60~Ɠ^-n Ec. {K: h$ K!z+&C(fx5aKuc3_26R&:ў%#o22u-$Y.pV:3: +?:^?>ڳ1RšyK-BI!{$ۂY);J7uk;"T:S%4JЦjY9Z6AHФ(+<d`! rJE +Pi >ћ(M}R. rU ٔ g()+f\s;l 6a7eA+xOjD SktRA ?Rn+`{sa9 )X7uK\2agzIm0Jx))D0csK2:ڟ?tS_K5L<_̈́-:QYf2| ӱ=[[BӷadW98ڕE"boF8#p"]!eFX `a^[hG.$ua 'a8Ǘ_O &X PFزf;в?}P|:-lSdEjo>Wj'+`oݪ9diXǂ 5vz`6|A&63ՇuAF 4*D`U*(#dfQq>syQO#_q:ȔE9%++Ԗ 9@,:LY*Բήnݸ!D11)9X˾vwZDnӭC3OPA]DaXPVG4/J'Yѥ7m-8ж(G'@Z%se-k'Xc YBH7xD$hE_J愼XxoHG>S&2x.> stream xr6इ3͙z%q{I:J$S/H,۱i/$],vޝ)p}ѽw>"JȢdGlc(8 b{\C˂fn:dEбu/pN1y˖3Jv0xGlKD(7Cp>(msqPP`{BֆdW4+tf]31gH nfAp% q'10#㉁&d2S bӬZvY}#TUn0"|ĈqIlVWgEI1Z=]Q vm7 FqہL5-*ހܳLj<$F AUYTY#acJ!ɀ/iLG80 r hY  3x|Ίl%Z J!Fچgif;TeKٛPOEI+{ޛ6EMs(M.e?R # ST0u!0Q`'lY[7Q9CǗf.؛j/38kw:©mІjtDKzaZM/D #̇ {Llj?,LR9BSv5G&>ȡBE_]مQq"7A{VBⳃ7qdS`Y8V'Cb0]~X#":VjS$WEo:($4ACVo@l> stream xڝP=O0+ND;'N⁅"Cۘ4CC=S F, Kg=B 7@P TqYdJTRyrԧ7eASFe;1q,\t! ~KWwanm;N?6tb> /ExtGState << >>/ColorSpace << /sRGB 49 0 R >>>> /Length 15371 /Filter /FlateDecode >> stream x}K%q~]JOw؀] xahaw<8|eݼ,T-V8 x_׿}[u|˝3qϿ_ow?Y???o?^럿 ѿ_mCJ|}lY_\fBy+~*+|r2.ǟ qƒ/q}>!g_ןW>$>y79~cTJ6"EوW84ry4|Mϻk+fc?V^g?uԯц տ&D>:Y}>Ml, ֿмڶkJԯm+Uge%V~M[m=wRG:wuG׷ |v~{_L޲C:u]y@\JNy} srWFhj S=yCD{:T ͂vC1t_W~Bּl4*fFڻp~Q{NۉY~vmzuW1*}J?~^}}uʂ< }e_;%Ok~*ˍ{0]u/uC~(i/g!˽ Ʈ߫>E*qN!>_e Y~VGm66JG4o/磯i}A<_aLL>ɸl|o}Q(ec|O~/i z'eyu8qտӒ{a;zrgMxKJn_? $]O6?6JͯiO?οg7x/~SeYV0~AL\Byh7OmTwK=sC3Yn}}FcЮV4 MO@]Oۤ: ͂v7LLmf+YcC~BL =O !BMZfEy $u,Ьh>SB E 3o~@LQj&fEZ|;L.Gjz}dF|< ,U2>Y}{w0찪;͊Nj]ŢV~.fE+Gj]^T743<3Z5*g܊fF+Gj|!Ao'*gxB|WjMѵ xxýZGjMڥCƵ ͌#N53H\SxBDujOF o͌#}^ .|/(g43]HpIMsOhfZr5xp>n}=u|!wՊfA`-]=^8Jj+`*2 m| GiM'jS8*Bahw=#0PpYn}MI͂vC0R{MΫ?I͂v5fs8kSeE *uWĆwϙm'fA66Yn}u4͂vJP.cyu-rED )yw6}~ED #-v3 eV4 MO@=0q v[,h7>Qua{îh|z:"fAZ|{]h|u7t۬h6JWYYh|uÚnElΤGUti͂v#.盝V%khfmz4 ͌vvwR,mOdWjR{ ^s#~v3mFH hV|:K3/o3.Gj^p-Q?!Be4+]Hzࢲ mu'4+]@/QgY%V3f4+]@]L Y}F꒑z.Gjz=_r(uL8yh7>Q#uy> ͂v5PrCDԱbNRjq'4 MOHqSgj' ͂v#5Pxjֳ"3jfA'VpuƹY}F8OpYց;,hw>RTV74 ]HZL$H͂v#5Ṛ%bnh|zb锚L.Gjh>;5{Z%XYS#udQ ͂vZ|Q?o"oh|:֣߳m/hgэʲV#YS=/yygԺˁ&]r|,hީu9}8d~>Y~&IqYvdxީ=iFʚuyC|'$bD>kYZRfiR,h?H) K-.Gj]ON9fA>Oj.y{@|~BH㒣nN)|CGjhmfA{{e4+ڏJ&$o`ffE_Sk~dV䔙G~|RR$]h5+?Y}qLUTaXЬh?)拔,@ÎԚ UuhfaZA#! >h*hf~ؑZ# z~ ͌#Rk(o?V=d743]ZbVD:`UiтfAQ܅Rk1'HQ,htj-epY+G6JI:1.Xe͂v#g\~HL͂8+b|gTxhfu#_48?Tnhf)[(+EuuTeE_ߦSkq]og$#֯[h?Tj-cL)hw>Rkk ZmgA(VXb;Vߋ4ᙷY}⨑ʸJF.KZfAQRk1k5LQEhfu:?kGJ`L݅%>ϏZY] RxD3(oBҀ$e ͌v#/iԊfFQRkB!WԴ‚fA'5RkyRR۲Dwq~'Zƌ5bVfF'5Rkq­׊fFQ~Rk(,q t_hw>RkyRT.^͈HrB1fLir Ќhw>Rk!vvG3(V\`UMsAi;nHA{g<|D3(ZpˬkhwGj~cO^nE3~٩=W3|!tW43]X ÚÍlE3ݯ{Щ:PIpBD-5su(]nhfu GjoF9_C743z#5V8pG=vhw>R{K-TjfFڛ;ҠD)pе~GNUV:»&uC3D-5\9E-rEݭW9RDmZ=sEݭo3R{K]&H~{ַ{tC3H-`jwJxC3H-U9ouKW43]PI qv<-df-ֳR6,-%+n=[e!z٬PvhFjy^ڜk͂v)aUz)խ+~N-}sjvZYFohf|Y!lԕX*|C3H 뤈GϯzohfFjo o5zV4 #5aY-*"4OʳBhu Gjo-'|4ܭJfAl̯ԏY~PQ{^zu?6fAڛ]F4],hw>RcU}C3H uJ/muUԼ743:#5VIyJy ͂v5P/rn1'(͂v5֧۬W4 MO@}[ YOh|20>-IgY}F[cY}ƚͣk~ɾY}հ!Rb W4 zԝlY6-rEH ԏ9+ 1+.ߏ)cڴxڵ q)H]=J2h>S] ,CfE`Q"ˎY}Y#o}4+=HECm+.Gjh[e*hV|:fYER eہ1dhV|zL͊v#5RGeYz<Ѭhw>R#0d޺,hV|:eׂZL=vE{FbΞyYS#uoլjzfENMwpYѬhީolFMW4+}wjVIf* hV** ʬŠfEN *~Ѭh?V¤U0b5V4+}wj][e#grةaUST*+>;~[Ř[͊ۘ`rLj:+.Gj]JAIY%͊3hUL*J+.Gj] QV gճہUDzۃUZѬhw>R8 f7'Y~v:fv+hvki+.Gjnn}4+VάV Ϭv߂fEZ۴j)iu 4+6VЬ@fZZ͊v#nk*׎n g:Wֿ^SV ͌vN;Rkv^T-N ͌#;mSV^U­F ͌vN;Rk^KoE3>Ӫ7],hvj/heZ]b+.GjQ] W4 ڏqzz-nzz͂v#u앿W4 ڏ9;fAZk͂#{?Oo~OovC3ݍw ͌wQl ٷ.Gj-z~u~@3(z. ͂v7}[G{͌~S%io2}x?.=젰~/~OOxoy !nX*~O/J ?MSfx<EݞLVB{Z&rn~ka$Y{ߊ. woM5|%g[!>_U)a˿jH˿)~d/VY}IoXi qc&1ZM}+<82Ez8x{ XUK Iaʇ5kMͪcB IbrI *b6YPIc}ӊ#u\LDq ;ˬLF|g}=TIG-dEb걕-*Ie(,u$W&6o (& YHk `|t f!-t"wE KO%&O.Opkl}5L5kB[H^+%(Cw/85m;8q1'85Oc>fp8DPӘOdAtIQe,wL+KPPQ6y#B">\lh]y _X78h`Ebhm:dХAn*C U>$, jz.;r5V \'Ō\*P*^wJZŎ5[l+.%b!&ZcV=HBz]šuZ1a=4yiّ_6J0:F$8Z3*CcVƨTFd͉?鼾# u΂ڄ=,inҘQm.P)kTN!j'MӘO(i9J$Z^ ':hk/>-4!q>k+uz3VAQ`Df6чʼm׭x"'"5qN )DK֜;Nߞ8=bf'%k Ysx9}Кxk&8:oP:qiP*Id2KH%^ٵ'MiTp/۝A%#7ͬ(j mm^1rȐhmhv1h9.5 Bd!p?ja(KTr=/A&SgG)NY]lLóũ8%U#RH*E$BNCHV$]" R[n$nT mvr2A ~E\km~hǝEKoAR#jP ,D͋f6kh\.bt^0yֲrE$sT,smXRG!"P#^K+ɀo%$L9vgO-^` HG*7@̦Nz‚zڃzcybC+_p1I$ +|D \ӠoVI2.;~$loU;rSO߽,O6ekUA~qńZlrFc :,4Y(]J؞H{ b}'2vȗ^͋ c RA>2҆\I.0PNb߁;ZV W0"Vh.tVbib,1BI(5XPHLOw1(s5$0Pf9#a̹rk\@#첞2`'}T,Ln]_O2v/mjJ¶II-0ԁk)Tq8?=C_[}QP"Fk)O'*d},nhXY7 Er޳iQD"۾NJ7a@VB_D;6Mlڞت|[VdVYm/_IG) .1r`CQn!Il)ޏn+":QoLޜdx22Y\)IX^ j+iiIXWЉZ=ϦsRi<@pP֔kFa|Wo34C) UdF^j ̈̋qR.G=3-h?Bn[@Vli3Xj(Ƙ,i_ZJg&U@CVWX>*#_לYq~2I 9T2eJq.h3\Dgw`!-kS0cfG%fD̰Ō_)eFHK]Fதܛtsy%-fzW[NJ-l>F(5=cj<] ;9R#s Uqꗀ.m>Ej?$A\Ȧ͔϶,֕γxYQ[CqF 7Flt g=xX"uFa#58l}<e<*$0E{v6Z- nflσl&bwAd>"x8J3iDێ9-^ՇD3Ƨ`cXU6ǃ hƃKe< ˺x4Lyx*=-TϜl&f@^lHA0b^<O(;qZ)iz>=7sVRfV@%yJdԓmN>5 d3mtQf>;taEVju}zLovo<y $l@Hkx֥ ?ǃMJ_穧{~V mxJ΢~svco,;k=Vw,tНeN%TP4ȰY`Nw*FSTwmki$=~ -]Q\@[}-_9|^`Lf#<9ـqM+g/v%Y+;i&8Qh /Ab,{se&i[ ںK7CxdA_ bֱßzvǜ̑%bq)DceHA"5V*"dbukә_l]XLÜC}Ew&+%cagwHbh /`Buoh, %O]ǜ&B ˜PL{ܑ,M*]e)-nݚEkY%& g&,GfbX빘%Kc${@xW0}bhńi'};zp[!qZ᱐3[l,}˝~ȗJ%bX{ 7i[l %Y*X0QVCWDM<>el~5p"T~!1kW4r=TD1g G1D;։B|"oI")C15WaV ϡ vw,8ݩ:\ȱ~-'仜FӼ1^ϧ1beӘO8& K<\nƃ"CZIl s|hF`#F''ۥNݜ/߄f:Dj,ЄI)Ep"桟WJw\wӝZ[$Uv]"NrAKM%е'UK/T]5wmr1ԅVjN;Xz?𭸻T5C#lO}QKZqr@:}XD>LWVv@H犝(Նע  C͇̰X`zOlJعlĤg|HTmW?R up1ps3@T֍)-"dYr5Ъbs-҉YOn8\9N_ER6[Kׅ^udx6qGVV)~ 'GlYΩ ۧ?*v0"יvX./9Nŕ5kdýRY> 5Ζ8vzHhv\wi'l7|Bji,O4y'xdȓ~˳F3tN ~KlfpS3\\lNDoMc>Aj3Mc>hW2[40{4'ƴMnrp权툴$g~C""Ü@T!]QSVrj{]Q\։ hl_KK|%PKY/mnqP+DeU1(=-Nz '@2t*2;.J}83Ozɩ6Jھ D2 5Dς<d>BD"Yp#0z;ܘ'BELcxz5HDD(7] @蕗F1.g="T(ٜ&C6ErCgC]9WAA֨jLe8qê K@5@!¾@zVD[ b#( Df+řSw^ar'Lm0.[""żo2 E1y[=ru"w'䗦15h'cdž+_# F\=xylO,G5X|x.5V$Q4s @ ⿳l.,+RȊQv\"!WJm1hPLm}#n$}Cˍ|%v_Re>bJ܇,.D-/o,nN 6u(lV=& Vԗ${Ѝwi (,1G]RZXbc_oK36%Jtg1 B1S@|PL9=0 x8}n1x=W:Gyix+)lr8YIODV0>sbF]^b˜w#^U_dұsgi,wm'{X\'+{WГH`,]ybLxsH,ը|q'J0GO9aEI췣˶^: 'tE%9FhNł|pS;!w"b*z%_0EY(Pd&fաt,1Z| L~qcH 3jUPYxd*frZiԠzTK0knp7NsaASkc y:yYD2Ae[jh%ʇfֹ&CJf XxBjY ؽloP\ dm%En-vu{;9F8*2W$GB :WAm;:3XOs,fNKzzυMGUrJu\3pLLVgQ fKN$hɒj^Y`$l ma-opOfLұ0,5$Ĥ&%)]([tR ts:swazJ#>g eϝ)%j$g,!2+ wL9Rv"rkrK‚YKTR˭$*ؒ?/JIDm Sr2M"zKše%r`В`th ex0\4jBGKl'd@iqbQaN}aZfTjѩ 9-IoƠ7RKǃQ'NՓc=RA 4w~sfiu)MGBu~O_y%n7]WWI߷ -# 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 306 /Filter /FlateDecode >> stream xڝN@z, M> /ExtGState << >>/ColorSpace << /sRGB 60 0 R >>>> /Length 15191 /Filter /FlateDecode >> stream x}ˮeq<{aKh  -0 ,?tÖY.tzPżXd`<>_>8>܉?_>~___ß'o~ſ?}}oNOuC?KLZuBq?c.3k>P>?*+|cd\<ǟ q}9kg|/_8d|?6<۟gYzeh^ mfg l_ ǀ&.1 Inmj,ë~'fwW݄.fL g"68|F8E]:>곩fƲZ]y\*٭*мڶt6m'lNh;yIi;Qgvj;T*pSxWwyq6A.y>O}]˺/ς,h7DRK^<dy澚z~L,h7>QsOg]Z]x=Yn}^OhQ{sMh:J k4=F,qn}!cJh|U>k .[,h7>QK]9~CDϽ:֤ˊfAZ{Mhy~.k ͌vƼݨ++5PKLԃ3V:CO wQ^ >giw_3]2M?c@U!>} kW`>|n|\eTW6v ?n!n7U>w !~0>6UGmm}+GO?ףiA|Øx#gI>}m}q=3o,Q0zu=9?T) C)XW%v{ƟgM /3~ I< ջ9Om>}m^[_֟x;8οgL"O;B e]qȡn!98RP-uC3ݼ&j^\=Y>743MO^+$˪V}dB=YZcFj/9#%ȮL-z24 MO+6 vfA\_hΑZ%'ʷJ|DD%Gv(\Ǡh|Iu`{A9&'joXRVfA{BZߧ͊v35PST*?Y}ׅRǛ&fE׫*Cay͊v#>͊v#ltH2YѾwR{>Uq]Ьhw>R=wKNX3fEy'.bQJ|>YZxA6 ͌=9Oj o;nE3ݕ#v,h7*#gxB}~WjMsѵ ZGjMԷڰC ͌-N53H<0SxBDuiԧF o͌-}^ 4*g43]/H` IxOhfoZ{`r5p>n}ֽ u|!w{֊fA7`Y]/\͌vכ̽LxNcUWzEsD 6WeU8Wz ~V4 MO^bEOɺsXtY,hw #-.d],h7>Q:;~th^ZF],h7>Q Mh|%z;ik7MjEDR?a:^]\,h7>QuF4}EM_,h7>QH+eBcno͂v5PwBV4 MO@oXް+.Gj{qRriU6fAovZFJ h7>Qk{F[{S[ jYu#uLP߱yBnhfou/C-Ev/ ͌vW5}Aw؊fA>q|t_$_fDZ{u?,_NyC3}qAؽ743xoz^_E|r1@3D#5PƘIw[fF>R{e.uP ͌vn3Rus=bF3HQa1uHnbq9L‰❶YFj8-S^:fAw1Rcq=%uAYh7>Q{1ڇƗRИA|}F+1b u]jfFKlЈ#2-<}a8[ϊfFk;m9[@}@ݍP8&_Lc|Bݍ1\HD%_fLcwS ͌v#5NHl^(MH hfqVKmHآWb.GjЖEcf$ ͌v7jF_$ko[ۂfA1aC*'4 N%1ᒱ}PfA+j)Y,h:5Pϸ>pߴ+.Gj/8&, fFc·K8hw>R{ITD[vI"ohfq#eSNQ+&'j9o/K?GxBݏ^Dh\\^7Yی^*r}`zBH Գh<CD_Y}_#ց*$QQ\,hw>Rc=M*ܨGRMkEH /j̥hw>Rc|uW1bluj.9b =-Y7ňfEW9RuG+p{D22hVq#5UB?ԓ"H)hVqcJS_b4+]O+'*خTv~_vj5gxyf4+]H]^> fE3z4K?Y}x"T@H.*VBH OzuVeQe5SaFH ŧ̄͊v#5R&4=x}/ hw>RexzCKffF˷`9s>,h7>Qu9,h7>Q#uخ[<" ͂v5RGԙ%#|BH ԽD.!-5ڼY}Fꉕ\g,hw>R#uߧz,ROh|zC0XF b .GjJX&$kifAKђ dD74 ]H=1 tJMuiV ͂v#5RArʃQC~Q{ ͂vRǷ{Z%XYS#uo^ ͂vZ{7jm74 }wjYkWJv4 Nj}ϳ@GeY+,hީ󞗼< ͂Nj]d.9uOh|ԺϾgB֟hyRw\V*3^fAwj~Of],h߻I6I6oh{c*L'4 :R:mJvRDUfAZS[fwYоϓZ$os/,hw>RgS|!,h{ǑZF=ګ8,hw#~9fFԺ"f f![hw#nﴔԹfF݆Ժ*Fw#.Gj^VehVH* &aDiXfEZӢhjhVoiZQKĴ h͊vEXf,Z `A}>ԚoBroV*hVuN:A]luNNY+?YѾQj'%լޅVN.GjtuK͊-RkHɲ~4+]?Ha{^RkqV*iTX,hߊRj-̷|FuIfF_7RkuHCu^ fFV|RkR[TgPO5]Vm:WݥvF2݋-S{ghw>RCJhf|=wيfF_Su%lfAKj:\ (]nhfu Gj/F9_InmiQϦ*743]^R~Ḣ}bw46Q tmfFScD4Ih7>Q{I :-A\,hwU@=Qn>V\,hwی^ RnhfmFjsGct9hw>R{Io-X6S{5hw>R{I$[͌v#5tC\]O Y nhfFj/鵔F tKɊfA[jjYi6+D+n=G׭6Z1vAD%url^fufAS{I*}n.Gj/Vu%#2 hw>RC:)knڋ+{ހ͂v>Hb2RӚ"+cho%n^ɗ>]psEHBlh|Vg*CE񾡙}:D؃sjnґ<]]R#u-I^1[,hw>RcMf=d_,hw>Rj[XCkl+~=N ٛU,QWY}SZ󕆘͂vSDmZS> gjŢ!Ih͊v#5PkLPǫ2V4+]HfYER eہ1dhV|zL͊v#5RGeYz>Ѭhw>R#0d޺,hV|:eׂZL=vE{FbΞyYS#uoլjzfENMwpYѬhީ[6}+>;w$`+mV4+nulYeVeaE{{YἏhVwwaêgt+>;.rI2͊=9ouNְ)vXU͊vZ߭b̭҂fEnmL[j9MX5͊v#Y ӤЊfE^guLK*IEZ͊v#[[w͊=}YcghV|wUU[Ѭh{Y3{ZU4{5Zմ͊v#~pb {w_ggւVoAHmZB {vVVQDffEZ׵zkG͊={իT_թVfFk5;mSVfF)UoѪVVfFk5|O{륷Ѿeoikkh}B;u uW4 ڷ2.zyzMfzzM͂v#.+[VZ{7fAZ:|+[~^^^^^|EH{fAן7ާW]ZE;H^fFVE;(sQ ͌v7bmz[͌xo;{zw͂v#g0wX,hߊ}F{0w5Y,hw>Rku3uE}+a^hfEݍW ͌WQ8{n\H~Rk[񴽟s{/-FU743]Zu#޹fFVu#`CQ۽kيfAZn}4 ڷ{8Fynhfy#72w껡Ѿ72 LdFRxC3͛|/r|C3}+_dMޝfFZ˓}B;shʓ}Bu{2+w%ǍZ~\{}G3}+?e]lލfF7Rky/03{'4#ڷ"{_`fY]ФOZ>l}J~G3}+fvm}͈v#=?؁:o?Ѿ=JGfA>RkﭣfFo2?W>!X`o<5Ͽ*C)q,DubG5܍D2ĸ9bE4"`t H]p5kfĊХE͘K'+,Z"Je]#ŊY%Xbe&8CR`g3O!<@WaYݞE4yx6'`N.^y6U-W yE1Nѥ+xQ_q&R [CvEGYhaO=5řq=R$8C\9Zhef8Ȇ0Y~9\򹕨dCpv.DjVWv/-AJC@ţhmKtIxxiӃimV$}-nЊTv Dg559li獭@p"cu"1?46 k5wa+M/c<|DkA 56qvDk'Jr\m4ӌdTvK()E3۹?o˪iSd* \qX Wa/k Qr%''֐NXuC@;5 J75 :}PhNSXVZ>l"pr.ɇޚлC4d\tD}'PYSnlM0q* ӑCbtrX\ܾpE"/$8x*8"m{xRh)'odZyw%u㠱&d '$휟y#|Q 0C03Kԭ0Gۭd$E&yD+bs@ Z k>S~/lN81DiZz>0e44⻁|bRTNWRK/9pA-89SB}fJ!21L ()wLپˣKKʦcq-i^~<<,7MX vxrZXO= u!aDSqƣ]y/x?]˩5ܿN(5z4NK5Jrk߈-F6-vA`Y%X&Nbپ1_/Xh234ݎG?,oD)b-NԻTf:/u\_ƜAL\}e[qǔ/3ǵ%>#sˡqjy-!xxyOJi<޺Xq[z2Kw&™OǾzNøE<$dNLIJ)6 ]/ \zg%׊T^PחX/( }hFED!9RؾCi_ϧ#Kھ#BR6py;7瓊ͪ!IZ읎qPZn됥Ѩ$!zv-@~ԇ秽tBp"-ZMN 9ԎE[9@^9nhe%Hoim0hCnJdlXJGZfġs*a"-"΋M١dHIz6Kl? &$wv;zo}^ Fm@4bzQK/uE2ϹfS֪Sf{؛\LL M5jGi+q<<߭e|2tGw F~"Y C6e;F~' ȗl9,,hOEƾI5&s=ۏ0]BĆQ=b8.Bi(3xMD)#G=-bNnw궪ɢ hÎv/$ ]F0i-MOR:k(Y__Rgʥ`F}O`Ǜf.1^G~{Ke. OH\{&qfk'tܩ/`'+T7ZNs~5=)ѭyoNg o'𶷲㘟Vaxc~"Hx}^>}e/yl<'xW`'`ExtK1XUq1`59MfCrgI\_ؐtc9h!HiF~KrIp)>,:; F ^4fyZYKmi+P䏱X!#Vldâe^<\πäIOEc]RK.HoqaFYGJX+x FCK% Y豲>o~C$T˦z酨H/V+r(F E}ʔMD[bjFP,0_X|t#(߰*N3άxtí0K FU(m~0/22/3Ng2Ӕ~ZS +~YuYdAΉqV?2&S LDq3\ `v53A:MeJ3µuv>OMp OH:.CgX(&8Y猄('7#kѨ 9t1'k.imBV yRawA>0~cfBWgD3V`\BK0q: ~vi>!)2QۉN]'iaAqt>lX05j v ~Sˀ$~Va1ދAr*FE .&tUICK6'I2J?+(eN.,_`DJODN- 7 3RK\iF=޲aB'PS]NLƗZy? RA5f7A2K/2cce;^+ ,?jZm k,VAk$ND`W(ඔ"y`ssPy4Ue]jjʞiAq^*NI̊ʳ37s. `V ~I>ؔZӤzȎ5鸲0+ʆ:.n`MpkQhTwHMAf1оP"C))L7#0g9ϔ&7 'Hp쿓!ڣ ZX0C}D i D0.0c?Q:!.pXkvI؉-{=g{X0cLT6 őj,*ƭKW_~V䑼i:'C'X͈8 0B~C`7 l~QBEqdcJğe=CМDc>Ng%k!~kF >.x1b" sņ;6)TxwMKbM_2y}*;"aO³ Vg'UA?)Lj 2MZllq kH'(|YEYjb<Oy}{R'&vpwJU2r}'L+C)tEI\ D"iz-BGMދQUhHl wLe!%Jv9) 5/<떨čS['!$7l!_HU ˄Ō.jkZ)>WpQ>#E.fMCb5vNYCX9}e-i >T@'1G%X0'HGs9(i;ԍk93RFhSK m/ f{$,ԂqpBId;+?ʛ'3[5#{MAszB5-L]-ıXک2t1#\=^zvF&ͱL39jim@ %}3q)d3s%$P\L頶u2ؿGu! '߲(83}{J)I:w 41 YL=ĝZ R4 ;O4y ٷ:. 끥* 8 ,;錱/7pFjoi XsΡ۠%HH' U~2!z1FKߘǃcLL+xpti<8zJ4M=-g3WOƃ㬧$MӜRxptiO--k.ʞ 7TXZA3%PoxX:e{:kp<%`PB.)AĠDN&S9beJϷmJNSgrOf u.I1=U=Pڅ*Rv@N i-Ye&٦cPC^v/ &FىG~rH/Uw7`ϑuu *PļncB/)j:Yt; vexA2r5c! ߜ 襌"w821^kh^;Ci "Z䬌K ) A0 ?T~w j. tXb:DGjFZ BJHeHP'A_o5Q*`/߈CT-4Ǐ {n ON؟SR5V_osi:X܊U"=}cΓ!jW~?mK4Q]#ͯ4[ue=~= 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+V9lwC q 8M@mz8M*.T֬vf1GW#@E9;B+rmCst ߑ=0{h$ܴѳ_$3_ט"b%ӑ测fw:&d{oJsV xI{veUW=ữi:eT |nWuvUQn&BcʗL$p$Oæy:18`nqnhsF 1iCl)J(%Y IfF endstream endobj 52 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 4517 /Filter /FlateDecode >> stream x]]$5}A+23ZFB f$,;H`W0V׮v'>դOusvSw >5j0OjFq3 o,/AaF !dg{Fy Q8˙_EW`w/`N7#E4‘h g~Fyq*DE |47~ FTx/(xSUQT j`w/"N_0{MVG1_QybJFQQ$$ٝ 0q"#iF./(D&FeGc`vD&Fw4FFv4F FiGd`Tx ~FLi"P"|L2ʎ(_Qp%q?@#q?D5 w (`6+`v+`Զv+`?+`6 +`v*`Զ*`d*`6.*`vݏQw?F}Tcw)`Զ(`(`6x(`vA(`Զ (` 5ҹFQ/+`8 5"uuJuurFQy)`ԸP 5FUQ(` 5FIFqFcԟ>ޏQDw?F9FaF퉈FFQk@+`Ժ VFߧQkEs0=T: d95"Qz&A*V>VW 8ZK^( 8խN} S_)) 8SM 8SN5*SsdW%Ygs$m/>'wz}8iuD'xן޾[sh#<8ĉ2' s:=[bd\F>b]LԴ|ϓ^-󤗱70Ozm̓68r0M00W~:MbCEz,SWiU]Ldǝ=^&Pib:g);hۣt%9"= l爋\e@;6Ҋ 'U:kMBH{9#͂~UVX~Jg[ +&2s=Voe$hO$<-{7X{5Eiqi 80_5l|vp=HgzOx)mcNY;M<^ tvy'a%#|;~jr´0/E$ĸ.e$rJ|^6XELwX1V&y"V3qzOE+=pZ;Lw7psԩ vv\΁yw.xQs c+i`9aY6h;WåX q_6R:{.sla FXZ/`Be u/D΍w]*LiX|}2 E}2 |.e$tKe2 RO´t_&aY 8 RB̨ fT&TO#xNJٔ+=&%qS)W{̤C-1FYe pbyfhe01r89L$͵`Rϣs$.j. K >MKdM#Rn靌j1fv*MJ|xA6&u)G.Q-ɹZIS&JEd?I^ z~JC5ps tQ`Ry߅a)ɉ)q Iկmb|֥(Oϝ9%fպ>9l|S\ǁ*9>[9r3*M]\7[&L\7KsݔɴrAs)M!J&M I 1r ځ^$kROO M z8(V09SC<|}p7_~X\>?/޲X* ?Ik~#>Y}fP:CpHK P]A7(}WPoQ@nQf<{2ݢo t3I͈ˉ[ٌxA?X7(g3RAA:1. >F2^Hf{$slj|HCNǷvÛ.uxrw? '3ċrDv8ȩVy;䄑 /::ϯ.gY w  O!MXOul͏{ye.۝/\p!Ͼ);.|z'jNDzn>@'~~S"k]JBK%r{1_gՋ}O=g=9|_xNzy<9w~S'?_ǿ *' 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 (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 4520 /Filter /FlateDecode >> stream x]M\ ݿ_q6&H,,b$-;(K^ݙ 0\Hivso6km/?|ؾ߾~v_?tyE7iභۿ_/Ec@c 8“'&-R>0%惩G =`5i _STkjptR?O?׏[Bߜ+̟R?;k0^G/{Q=X|r21hS.§)،)8^ǧ>%]O!'O5*S%T]O &i +W.ň+9 Kr r6^Q^jrra䀷{F.j`䢩%K0%/rC/~F w ©_Q^Yj?7`䁷~F؅UG>̫_Q^Yb? ~Fxa*(D50+`TLQGɯFE_Q&M/(_Q1Y%|R^_QZT0J+`TLTG;(xa*(G*i`V/P;F/¨xUQ{)T~ Fŀ?*T~F`Feմ~ F8T+>F`ozc5`Fml2^-j#$`;Y#pd@˦h#pD3NV#&hF**y?`Fm Eh?D~FQ0Q6!/(DSTQ&hF ԕ 0ة?`Fmb_Q&ר@,+`k Ш@0JDqHee2Q4j#=o"r4^lFmrYYbW%FřQyaT"O\aLѨ@)+`T-]aT)~F5Kee[b0=#oOY`_{kQQYYDaqQ+#VD&F.EFee$O_a~FVFFV F Fye*Oda݊(Ê(XeK0+"#kF|E1 0 iEc`W/D&FѮh"LbXee$èLi"D&F)hR\%IQL"]eXe"2 0ʑ~FٰD&FeEd`TL"_1J0+"DU~FVD&FկLj_QL2 `Q'2I0 ֯h(ظ1J0J+"D9~FnAd`FtQcf+` 5F{?F[~J1:y 5F楀QB)`C 5VFzQg(`H ~vFm~1:vآcK٣QQm(`j FJQ+e*`} Z5(,`RD;N ӑ(ku 'A:ײy'qΫӑ*tO 8YNZ\cŪӱSX(tp:"NGQ 850a6RVusi`+}]IX7(o'k}engEri޺L+h O6o%\Z>_ǖ'K]81YjOr<%`pѭisڿ-+*B׏(\Zt":tYN&41:F@w8\ h? ::Pd@Y #gd&#TiU߮˪t}]FkLu:o_'uLb e'WEIaTL'!ǜS1Ig0Ss+tuNJe@;ΌS%Ii,SadV-eBG9z?es0N<Y q9( թ%AFm-i zixOO+PI!0IpYBc@&k9]GF L]HTD&K /NXV'ĵt[q{)t=m;?6'LTiyil4w &@x8JmDKJ)axL`C3S9g%6,O:eQnv 0F+qq-ՐGIarT tVd&}tn+ #N'Va~ $NzYYh -rVJ ! &]!]bk 03  8Le"U{w<+cpv}2 pNt騍.F< ” ̶̓: ~ E*V&Џ2ipE7oHx` .%Aόڋ@OYNpw=Bc^I?~᤽d ݤms=NLfR -4Xi% 'ſYfO;2@;:,MjenUddJe.=S_ )OvM%z#613%`dSc`=+L|r 5Oq,`r-nl~&'ȒL7%Kָ cRdx&{ ,z.M&ژ{pYd7tXyRɍQ8`ČL阮 S' lLwE8ng.74m nf(]etCeoU.KN^lL.K.\?$S<:sԪCSѢU a0LirIikq,Z -&Oy={r[ B. /NIt*%f6M49`̄a:|=] t8 .So>5!FQ}Q,Dhpǀɥt,+u4׽XIG춷*W尓ȉazoGߟOwym$cw[[8I>k|{g݉Ep_ yZSn:{L/=bG n~$pCF-rV#];pCF\M9yT?xL$iFAnjf7tjߋxV#z&25Үck4= {>ddnw ѿ!uvb$>vrK?NNjy{uSJn/F"oo;ywe_Wi|ԍx0m'< ;v8x0- XOuf19f21)r᡺鞏6pŰ"JwמE%ܿяaRDEBkr}-*y4"zkI3{AyAs S_z[n;9?^^翇R` 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 825 /Filter /FlateDecode >> stream xڍUK6 Ћ D ԴIS(([EmIΐ--M/}3Ca+o+I8ւ Ҳ2ɡ'wr^LZƟl)qbL__޾-v㘕S;( ~Oja DgƝlJi|ՠ7wTE7*r9[Єjiev0Tl< Ohga.TLyn۾^S+{Ͷ a<[XeK\-6~h80[ ┬:dd [U#TϦ5֭8Rc*7. خ=-YG5R++s{CDpU^X$ v]! qi˻,Z*xM4\1Jx.qCPWl:C6- Y> 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 1991 /Length2 21989 /Length3 0 /Length 23241 /Filter /FlateDecode >> stream xڴxeT[mpw];$B hp.}F X>^5F( ;gzf&2Ȏ^hbc`a`bb''q9[D<Ng {{7<9@ht|=r@g#U{ 3/CLolڙ[KD@zZ mdb rsٙ w% d0Z٘@fU&@MELY BX?XDTT$Bb:@BMEϻ*9@^=g{r91U!U-E1f?`,/l^jk͍ٞəh`o>U K'E;4s*YKO8Aw*ߋN6H81ZYEEY3= `hJ@r 9k? ޟLOoc,0A, K˾ ώ^Ύ_ 8lwٙlmQ;O'gYڙŞQ(%?.́&t7`3/q3qe8},̀^NF@ 9&R.u3~G꯫J~OMAv6S<<]Tnw7R/N;?S+E-*fIoKh#pwt?İL]ꖵBdntAw_D$Xs~{6Z5 دз@|G+Dk5l$ =鎯z,\2 ӕUD+h&g$N|Jk+>Ʊ`X>(vRe:H3N}*3 NVbC2gӰ7!JX<ĭ\ԑGUJ D9;E'Mcaf*OT@)]}c} c>B/&,Gŭv1>X橥D^u;v/^˥Y*Q35bx19m8dtqO2Fdiu CC?<1֓gfiZ ʐ oZɇ;FBC˞J$O`8jmbrҁhgwGZi-XkN$ULhoGlu`¸zhQtt!h3M^k aW* dIGGt N&&嬏C ff46I{%_ 65vk~G'YH F} k#3H|5}Bif< s+oS͂,S705|ZߙJiê<:8.}G#yǡ.R>M[=AǓبuK_}n~3 v`gz-A*uJP  _sXJWQ̢BiT~f獡]9=x` *F0ON0m};|ZlL ~[:5UZV Vz#/$m#2|)vv:Ix g4DޟB.8AŸxD1\l"Qе so ae%M*&P3r=kܥz[(}J?鍋`*ÿPvX69`k2%p*4T%2,֊oXΈ#ckzP7H9 \t?TՏTEƥѳ2:*-rw@b ҅:TmgDV$;%;L"|n?%Ռ=W71Q}̜p!ܫ@%z)}4OO}xi+T⛂]+|1и$ZA(÷Uܮ܈NfQ)߉c{B𳟉ͫ)? MF<)gm\ř3=F&K s1a،<k]݀nKo 4wGrC)bz9Jy#3œ&hTz4qXft Ա O.FyH 2)idn\|p!_K _Bgm9T^:'Cc`E3鰢 u?JnuvU-ݥٖ'~M7 x! :1]TqIU:.yCbuꤲ5VwVབྷcuxQgS NJUFB ƴ`zԂZ }m"Hw7O /fqbGآB*~vLzZ{f[q1 TKl95~|]9CUzuʺ%귲Ǵ\Eu^*6A"LRly }t@@wlܓklu)'C7Eesqm͠9> W[K 0PIW7-4hC70=EdV\|s﫳8F4f[g׃C"s5e}GRɽ ]h^1XY<Ҥ2P%jYQYwΝg2-Q6ysCb,Mq GA,h_L3q(-4&;lU!aTU/17ye'{;<3{l4!kH>՞.m2[ ONFd 63/LUN\| ?۝ Q >_Ѱ=%[L|tjy4gP; 5-gZ(U1;D1'Տ֗Dxt 4hj0),G ms^VerY*9 J)Pf +;Uxm`\G쐊Lr:IT  4;>ǜ yS G>|nTWC*阝[:"~ɽ^Z'_mUgoMƀO(Lx\i5KﮫFI3.7N-(b"0xh<(сG!hd5tOUd<+A@p3sE}֌x)$}P-| hEnh4٠D%R 1SX|ԕ!9 VOF7?(/,ŽndRi2ri@7i'X -iHDRXBs-vrSbd8E qiW y3U>*l.Kb5R!E-i4GQ}Mp{9V$Rnp+Ez~2dӽ/ 7V2VK& 1y+κWW7!qVv8W*K̟`hlu=2eJXlX@NLX*Q21FPM8!MeQɧWU1[RD͆!@kVk|Æ =<"fꊫ270zdlLJ 1WF(lM|jQ~$ZBcx]BQB=Z:d3T&GK< H#ļ v \)a H7ZA5͋UG>`5oɀ "е%~f㤾y<О s Kֆ>,֠cYѤ~Z&Dn͝-]-CFJqwHۤSiBIqF( -̋΍eL}N>tIiBIgHy]ɯUJ6{f'/9{~}q8Խ3$?0߮ܺp OӼcoJvָQ߱Kė?Ph_*R96c% _ 괅BO1zwz&#}hqU׺UTߜפcp@*X~;n+r"@w;=j:.0e!ξ>wGަO5ن= +ӨqrQ,n"/<"GL_/Mkez 3jNѯrYm<3E(J%$*Q";>$`PFKl?y^'Q(]~#S}.- EE*,\DžYC&6~M'"^o*~c-Y.8C!D"` @\jڨ'YVnW)ݰO-Zcq,âCTIN,ܕq+=h0Ǘ&zAqGs4!#GW_ &a&Ftf4W%Ѕ̵%k9^&3_8E kX=Jz5o 蓏.0iMi!mz$g?.ԂKOt(^[nYR{RvqڹMlo_tmo`~x|B.-k7Qs 1wgTUtldѩ|Se\+%[ V8 T{@# oVչg(dO2"S؊C-nR{&AJŢ@}l4O><)y;?Z:XF80%Pӆ(*7K|QȜ_3}5Z/V 0[a<á/*1I>Y2HFK5HۖQeפ٧ =fՠ>aOޛy 6)1']`ҸVbdYlɡ s tQ^ò(O-E'2I_}j'w ;<ϷNjaDCCӜeHۍ:M' 䚐R3^zim " Xu02!W⭖U\q1o+v'~*sӒ~a"HqwPr6K5%8LT2w&UD;j`;8w<ޗG?jFגΜf!eQ9EMGOjc"R#K0׼k73k?Jcϝ,sBjVK=mvLq61~}`t{|J32v?("3,%q*NM𩭰>L"=V(K5<f$"U|)OC7?AH}FC;j˗94LPԥ}q<zNj[_\KW6?r~2΍h:exO;W]-)wcCߙ= ,\hpXR NxCFYڻu0? U: qӗE`=u,c\)v9p 0q+[&wʴ(* {c4ق5&WT'K-FLNMz׌"2UkYQ5 %/YrӪc`I&Um"qrs5pSHU źٰ~Et]1̾c4S|礋UQos8*UQր Α~=ϫh\y((N0TURj>2O뷪z{I3Ϛ>߃vN;>,|P9]nv:5~~/}8dx[pBGQ>swZ07\{0.}e[BtV=nksqX lfqq#͌`~K,"EP` dҟ h&#/fɸ-:)drHz:kkvi/LNEӑAjܓs#^Uc6+⥓_> Hf-MG+q] Nƃ`$<:i^'mZ n#:G}M+V>{Ͳ5Tj0"uvr(pp_TSȢOU:.= [t6j'\B`4Ύ B7UvG PfVZ2!uΚƬ[O? LLʆ\'7'QcyXo}2dyV~{ܖm*wlwO*H';((5X#<;*Gx3$i70 9ʷv!3, 8!XY 2nD-:,Wj*qSYh߿/QCweHFNᢚ`\&%Ϋp|*1Xo<'x(г]ߵ%,q@Ƈr}TigC^-:_eiHɧ=q8OQB~lZPz}jdռ++F?Gn#cҒ#U2gڃН ن 闂Dޠ8&`_,TxguШM4si[ɔT*dwJOyBaG8&c͙mj }_lu63 Q:Z7.Va)'%\`aGZX.{C҅둷+J7m1 =ou;z_OgnL":x`:Qh'ʫ*I/fqg":6x#Y@cY:nx&?"=,r3 \彧lgJ:W}aX zGZxVtiI$z ωx9Ú/)ckH͐D3Jɴi"b n!tF4xRI$S=fB T(ű~q%o'c~Ō͢74mxsB vGVS ONJEŏnӬ0V[Y+-Ze8Šy@Ϥu6bbp8V"'-h3O''F54B+*qI1D+р%c } [狼A5Fަ^wDoI^p4Fd+7xKGi"O<~}A`?~8#wyu?Kةp44'pcxlN=!rYZD%I6NL3E)I}Ҳ{R.@Q }Izmx=[a_kƑ_/0h͈0 taBh:;`ar%+)v .; t' u Ŷ /toV$~(:PV1>TyQR~99t:UÁ@]_fǩwNJB~aIi= FXX"W%?H˗=_iC RǍC968̭Dv&h1̀E">JܿCm"Ǡ?@OUqڧ~}Bx߰ckACϙ8[GZ[:[Q_>SfoBn 1U _ZHxD$ ɴĨ_?g}P)&ˏO-E>0PJ0d=3F7و/E&lɍ9W~%9= :q%u=ޔ{ahԂJ5[p(u=QA4h̬d =XDž‘g%@"r/-6~;jjw8 =J$S3|i%%I!y3VA}qRnڥ3j ~K(#Uq(_R\P7U8+nTA)b+yie _.l͎FHedy*ms'W?/EH{k0)id|=?fA{0>:SK1T1k;P~dz}^~ b k_oO2Yu-QF*%K %1@1]$ RAR*|0!DmGF^Z`JcAM.b%w,ԝ^r ""8L8K֛Л'9eId䡵'R>;P%rJ]4|R %:~e`UzO8<ԓ4D)!v+2+ڀ'e;Z`fq(pВve~aC_iE]E8Q@[a8xN?e =>Տv_ٴc%dߢD fdu1v`ژQݔ 4FJ4ƥ"0.cc~8ױ>)(*AKGM c"SK-5[uڋi㞋r2qC .p<\:RSeeuSZ~kmR; [Z}ա&Bv.Xf.G+ڠX}~YElO_&AG&e+EOud</_.lvJv"LޡUkh 1RteFCǾ 3c SLT'v E? daƟsN'Pd_ VP*096) ew½p7߈qbyعDZsr>H?zAyfp##dS ~7)3}C8WfFIO8=7Sg*Lc.% A':u4"}w/A~g d&A-޶2ի6' \!KǾn~,WiM_D.=ndNWaOm8AE>և3M"Zgj^tϞg:z۾(5ɽ-GLZHjmOh\fцrѹanOe.\V!ëb{p?KPł,ƈMF{ ,F^_-PukƛUrnבC%7J[{j"R/̮0??4O wY]7OQ3QFs-+`6*Wm6AqqUQ 6a::+ԋ^nJs{WMC kKhjE ę$ݢTZweK1IJ~P3&%c}=|Ft4KRI[<<*lmpT~?f{r $#VT%ҲKi-DEUCsgJ.v+n) k-*]u-(B7szg:m@ͱ%oZsEOzo~sF: I۠x7Z1o'Y4m;*:waZf}/&ԛA7=noOlYKfDTC7kg1D, )9D l>7Z̤y`y{~xFi% Q+]@+;%v~&8OL{z( q4}gCe>?xbWk>Lszvt2Ӹgxi@ܘ4y fJ0Ɔ5Gd"I![Չ^&bX>pP{?f,^Zkx@k~K˒!&K(O wNS]228yJR D.sAxʤTSzllbG^i G֬Ok~oM?FKߨ~iB& ";:gѨohoP4EsҾ0U}=Xo=_R8:N{10cLrJ Z3j1G?2U׍Vi+^0@w$߲;Ŀw]cTN Wv+QuSLÊ%_|)\x(VK2U uƭš(z.LwuJ@|~y#>%FMPPshߌe@C\3~;`I[;0B\5 1XPM K^qKn|i4SI`Oͽinu OꡪCQOi=*.1eq?'B:)ApwiXztj𛪷cQd 8J3ǚAAtCS[0hǴVA=3:>hv`̩W t]r(AֽƲs!%(q &j".s\}&jv-aǷfX_54TU6JoS7#A(5Bޭg'y|45P'KN^ΓےdnEӃ=kjEnIhIB<3NɁWQ+ fE]9hص`O+;ۆ0_ҙ}ib$%8j nG&7)b~=ͮxPĨ98o;m3rM$kq(:''d.c 8A%2hGBle02Jߵ]۷:buN&{wFשC?]U鞾)xئ`XS>AEug4um.B4\^\}(OkrG'DN@w CIjj {9ΠFZ8n^`/H}ф[/j/(SYla);|, 3腟>qRVE!G`tipnilOoy@i=+!m,\ig8 ZDBE .t*]W ZD] Ɏ훘_q=h+5fՕ4ٕj) ,f{iͪ\-1 bڛ\>Dz==L9 IqE(UXP W9ɌOw;Mdf%#hnOvIH9RĘq [& ǻ|F}"Kdc# M Mk*YqB[M9#Ś@NA'54tygB DvQnΒ҆i*yYW> xb aQ|AeS$< OdwD g"\äkp`*P+ "E䛻xmS]d® 8>Sۋݩ;l٤% r,yX]G{vRBeה1u8iP,W4p#CT7Z~_$SXyg8-n xnJ@4h@`:m#oa|i44kW+U|v(~4`Jrh5"M@hCHԥ.FF} S.?yuZ")T̑Bf:zE2k%Wmvb[o mim- _w>pVL*@HӌЩ:F7B'X0~2҂KvJ5avxZv"bM>!oF8>gYmSR^2h^ `z*-UPB\9N^놯զW>XҜR׷r^MYa_1ϙP bsLl Wĺ>ރ:o"N M^ o?l// ۥ햬A_YG72Ry"W\"6^6ޱFji5槽Ņrm<aEL -łZeecT}(e,5ִҪMI6M(5SS Dzg4\a!&֑wX(FLGdDV!,GZiy`YF ~3I>l[QQ<_E[@Q+jċ[OrxT6frC bSG>BmPyoӳCӕ%8Gq XM n {w<+F7\\_[ptbg~հ_g {_N*Yq6!lׁ2~ݕyOsj˶"8@51q X)^zȾ7GZ@fޏ#iDr)07 j,<2F?XXƾua =mWz.,5ݎ* HfʠbJ}: Rhx3[-i?iꉷ Ni7ˍ% ΗaI/4I= {r*&LX||1σyAYj]!*B"af+z` Kyًճl:*cefX(ZlnTYiyLGh&|ԔumЀ L*Z?xKFyƔd Ȯ+Cxn~F7ٺg BjqQ*yvwp] ׫OtvK ;v F4 |V<'+t {z}0@I~0PucAƌEq{J#NiU`{F [k`ɲ^-o1B!AɪXtm(Ry {:Y͢9vت6LaZJb9X Fv=%8j6uҴr(L<tl%"jFTHhҕD~woEވֹO:([$Iۙe< ;\XG30ˣGy%M ]H'y9{؇1n%sybyE*WvN r:e 6CQZbq?ӫXTJ0Tk@jj|Uƥgj~ӶO7C4~k:dI|9hKt.box .O=% d'b2~TRP ܐb6ekYth EΎzPnPFעdEm(ss$S@);z0n}OW7|n;t54% 8Ep`=_yuoN>ӥ|pQ]fg6$)Ϻѝjbc^ohsX!K!(/0w|PNiB0BZ$ li&xq2,$Fw.5^ni[- SYOMl惜W4A CXwc5yPleK+ ͗}8fnēwf^rsB/I<gq)'[l3 yb>?"`Fܔ}En_w ^R3jhLdB#~H83~E$ðrE7tv>݈*zjA3?QXEpYm8: bk:Hg RW{&]܅_S An48`2iE0)/C㚎YI %m2y[Ky1 JMn8ufF|RcI"?f"Y 091%'vL\69sZ ![8. ɐ>^c8kkʗA;wugLBr`NO'5f_"'O`nLp4Ո]-nJWaIދ8.o"r-Z-* M|訑hiL^0@ԹEJ \ӓ4 cmEI朓кvTp"ozܫ;YQv(]Nk`-%R b+;/w߰GPwǵ\Bʲ$NcR~}rI,YZ"g}E6%^a aǻ NY *3̷s b]͍Γ}c}/ @̞d PZ'm+ 唱e,E=c4H6]킖>r\>+-t^%5\ݕAG&6dr􀠄YB=hRU:K|k|sz~;KQ(9I^[^ǻbz Zk!m;lApǩ#6l- LQUxoWd/[c=-掳-QN \7c- ՞=q48~p9 4h`ҵu{WI3H:ցidD.kv2oP%Z=7AU,yw4nB0^٪6&ئ^5R!cvIupa`B2>73>wMИ 0 p3O״F{ Z*Vd|O};e=I?.ȀB XG:3>`?6ߢ!nt<׼ZO ̢~ߦE. d('H׈wtCQVO+894(M,<pvn93gLy# |њ԰KL_Ȍ0:Eva&r-V #E`+/ Ld q85t -2n@-'ڹM_i-oYӽ TXh[_?dOh`A4loX"tr'O ~2gY㿝:_I/s|x5FqS D91Vuz(S洉WS3M K5--l \6d.!aI4 Ո wV03{;G-*#5ƸHzÒmڤu+`>`tfXWU3¾G2:WvS䲹}|wJ6?Eg'ԡ|`K<# h%C'F~ƞ>!(^Fg\N![ǜog&?>XPR:Zy ޮY 1;&LKFl"щ9+(+AVrڷOce둮$'=pV|/Z'G++O"Y8*,, du(k<Dn$"a9FbƺYu> NRb%bOǠvA/Sj+pkߛ<:Ӝ1{ M핆 1Bw:Yz,J(|^6fx.!;D(^Q dYYɃ*MEį޳EzaV&c GV.! %I3ftFX nv`vIâ3&zѣ)]<Ψx6ϋfbF|%-#54%].C"EXt9OS< KGo"-ZIEp@xoބw (RGPVl䯭[VdbwKc@9;eX$zqǮl /\fRƤA9*: e*Y(L&G(yx䜂G,ἡ?xEmmIO)oؒQw0}꼙X8ݽR=e-p ko8lkafMG5 8W&@9"+W-ߑc?YC" Rx]_ 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 2151 /Length2 15824 /Length3 0 /Length 17125 /Filter /FlateDecode >> stream xڴzeT.RJpw[pܝEk)݊kp>w>޿wd$oϚk&#"SQg W&6fV~"fceu513r"QQ;M]m SW ?l `geCHA@7 t5rhM*`W&3S75deҽm]`b鏷3@bg0YJ`7  M-`KP.VSTQc~ v.Ҍ Q% IP UzoPxf]QRCTCWElw͟ōojo`h]]YX<<<\\V̎ӰqxoWg=¸,j W?{P1\R:Mobop_ ?X嫠p0A 7CWSW7_'Ђ_q7g?9W4K] 2{?S1S?j6\l\\]agl@Ed$5Ĥ~/?D%Z`}kRI8፵ ҟIؼmm{|/ O-Y4A6Nn@Y1~!-X@'ܚOz变~>`G vAq1u\݀~>T'BbXؘQA+, h:tog X-Xo@W.)7{{%S ULl,*LKg"e Pq5+(`bdffFωkݷcgzyK֕v /Emhʊ3we% 2[؀\SggS/$ַf`un0o.G7W?%ώc#HXFEo `QTXxF\#7?ӿћ荙++)![JK$VC7N^@?,d6o߸9 ޸ rs0sd2tcloH;q| VY!C7*^l@?3wG/ jXݘahl6o}g l{0q8ޖg__gF@O9/C{K),y;𰄺U|"g3+ֺ'6=js" U"Y:ԃ:w5(n2~7qf&Blr"'G+" X(Yc譽9?ʞf"8V/eӕh. 0O!5~x*^a ܹ;7o/JӐ(n+3+ t2)&jiϬPzS[jz|yե(d+gsWJ P!Mw*@~o\pr)AV~G Hft$of9trVi j(mG߲)_(YtgY0 EuB7KT&Lz-lBWqȼPe_r'W_ͤqG77STc0 ex3z:GPXlfϣjRs?˔cLjs-wJA̓I{I${xI:/T&t [Qay;hӵFfgB2NSRGMI!R<K hX1ٜ*V j"p#=fLk;o:>)"_h3`EolΩQWfaY "R6 vd~#}FM90UKe v{8B[r)W|GP &d6h?Qx3>8h@[:ʴ+ ҸPa-OWɓL Vpyyy&Bx ա0|@1,p35gu>OYva=)CLɢkhiG )h_e1xM3>Ƭu#knxyv\Pk/EezW*Nq{2uvj"$'dXc%C<Jr2 7EvS5`MMI<56 QxiG0&>/O6t3/w{Z\SYJ3 jd(fkCmf:y5KKVrjw6' uQGީnX-F`yr_mTE-7mphw/Ev?)}0Z>u2a5(o _x1\iUX1A_Scӄ+̛7#c8dXguqEϷ0#l1X^W"ClZv +ͼA\|bO݆pD" f;|+ETM$JūZ QKF̉.e*(XLnɒxt cSƩz>a|9\=&~),?d.*!{5xyђG~GްXA{U)ě0G8QSó[ ;Un=%\<7:kvɹɮ Q.zl Eq=cηPOp*:͛Uwl}n.#JK/dnz׍c=xFޙ|HE9|GtEȶmYEstKsd]Z%c$$;MRthZh.6e@bB\>%َb!!DMcC5[#RB=EvcH{tQ?8XJIW: 9bUE jrg7KC*8,),SOP]9 .Wi Sa𶵅JnDv/i߶ &5~>Wx_ՄXBlǕm?DP&Aګ":U]|FM`ں8&h]_" xi ~+|O3qqPA*(fC)k%)-DNNCaU.yZ NfSϖ ұ(Pآ.$ɩ|ŽI1EyG +R6L*.yP:nN| K` R6xt[Qohf |ٓQaI| `ٮNY}/֝lWDTh1+dWH0RS~@#xd\D,EԻC[·Szd>e c^x)y53K ^6w\"-$!ƞ*̥Ts (FU3fHi()^I2!8q[ʻCO q)]AZvi&t{'A"vrÃauO .ޖ Ӫe(nP |"H<%"(3:l*xk`ʊ/:9Ad '˜T'l z6]p}Ƭhtڦp0ƃ=Y䄮}>Fl@=̥.NemnN5KOJMK&O`!uX]qC5Ȉ)OC\L"SW ".D i>AH@~0!rPH݇"o}*cżQ4e(M߇1CSTex."BKGQ%fp`Dγă #u7 Sr3Z= ,beQٟ\$=n棍p E+zVDV8Q[&F$曖/-/qJXÜf;'yn33&Gbk"D 9)қ.x&Er2); 3c%݁:բlo|1&\R[ _z 7+ pwӜlzk^Ŭ8AhaJA5 - |ҪR1Su5/#uXЈsuܳOqn Nݭr޳Suym2EĢs=EѦKܕw=qlӥ5E,Nv&v*WsqCKpBTy uf(.m+퐌];\sZ*(f2%ȧegDE=HIi7'dӀ5 N&mB*r Q}QOԀNTHB>G7m=_eu)=x@Lk*M,4בI<0QϝKNqw$=:VPfj,ubs.LϬQi$"S/q a%iqm4ta'j8gކ#| $]ҟx=<_G|Arק_yR %6ңIm}, 'qKN>hd`NNviS nE[[6U4p4vϒ:oRJ5 || tƑ<1Wrohtf¡KP Ft)0?9#>9K :n@@\la^Z}=r_kzU.e|)ˢCG}nr$|Q~[XC / =Pa11#ZCH5 u<}zڡ}L;ld}#O4_&C,epp<<~?EA[/3%j udʾUtRndwU¢sda8<~ g Xӕ\!SB˭ÏÛs$~O 2g7BڗdyYghSQ7ʑmd?%z\fљa`LEOV,?6{\ս:2҄`C;WR1-)j@A 1>>CJX:hEd!qbA\ze4lޕꝬ ΫA@_dt\DFT1H*3U@μu|ܿ܅iw'lg=<E_'bЯ!2[>;i]g/LjU}Bq="6 Ҵ阜.l< %: |-VaE>h! -I7w1HjD< ?1)ņ115)5-ӻ2+ܤ_ MuUZ%\mev5VuT/z綁5 k|@tV.fd*.UbXSo&S'?޽#WeT8tb+&+lW4n(ӹ'^&>̹S}w:h;:s1Gjj6 m24C0glq.LaJa⏰tڳL.}F?8x칋b8D<:RJ&8Y,[r OjBкŚIg߻ }@#V(BM~i7ߩe\BԻ:-1"kE3)-*L>MjqgdClqx7k\Q[i!hx"o?YF!#!'[w+;>ZwsoRSvz}N!EP]B wQ_`-/b˼ӣL)+#ϕkj5ΟiSi[w<1E )g<soGjW/J.kDt -aP! #Ґ Q6{J۶uՒ`k #qvs(N#%&e e{:ٗ2UEdntPx 9lY !eA~+mHrTi~Y"-r+'ibݳoIRY戮r+u]TG20-DMA;PsD555| 6aoCa@Iy =\4 m&F/hYR0w`G(P6MfK|1;`yv.|ĜQ{k=Kym*NBi`ee)ӯ'WASlmH뽻=3H)hUz1+wuFD?şuIXTq,F/mIWo=u5Jqf5D o+j*+ڔ?X?:2[(q6dXߋ5`trNuʶ/EEA*0}?iH7 ",.pG0T_ /maYaO`ZK oyLO ۬,x6݆1B]\~Yi8' Ue5x$)۾z$j=G2xB&8Z )(H7a! 5|OZy{gɿ#e.ON S*ԜvKEFxmnadTodozIpSnx-=w_ڰ~YH"?xN,:T%t lQf­b궔O„)2S@rj "ݺx=ua-)y<FgRJfޑUhFSrYwQzv<}$Bgq);ɫfzyhQHP'Zwl ]h~g;$[6;\\JvN/)*RU_dαз? ?Y7 95!3^EPM!R+|r7SM$԰:rkVȀǏg'2x~įHXz%lip Aʶif6S=+YπذZ,<efS_Ώ+h=3ĚxVG3N*w$\v"z*£[x@̔2\?;33_;tRB863;X0=&B3Y1,$ ]F]"?6KZ"jCU (ܾZ$5BU(jy ҟ$xu魩B"}tQ]FMyUt% Ͼ0)KA{d:h82Øpѻ:L6ۛ(fF{o!!\a IBӯ~P>,ү" i yBH{!!`bq_eЁvֲIO|";)뮓 j<L&NhI(:JѪ(CFa~EdEG꼏 ќW+Ȅ]P_%|(~a25q>>EvWjuA,s &*qw@p*^I_AL6&(6 ?dAfZ[^O#p<5 VK4ipr\YGV#0jZ:(Q4e?%HKW잙^Qo]>HR=ezUCS ,^LZH =B|7cR>d&&xsӳٲ[n6lIO(w sƅ}!NrtbXղ\QO*cgAЮ",KG˪? O+bot[Jk8k%)<2Xpu W[} CvaT4y2 6A>%{Y fZkϣE,Q]!ZD%T=tR!ja]d͛gK<o6QRnRua}'e,NC؎rem:ƚJ'%=u];Pr2V7)K+ >şs[\ɶ4s>|Y;iE;zSI#Ni>}f+Q.23 |\@Y#{2ܘHeo=h=sR[|ɕk*AG #[W'dȜs@*] ̲ Fu!<x9]gu Fc%|/"[`݂gtSSkIe p"tN>&0W|[XMb*ܡo齺QSB]Y1[+^ϕ)ЉA8kLgu҈ k`-gǓLQ2M=(yHG#2L DBRsM-O[V}l?~6a;eIX2FrLQI{|Y߬cLˏE&EP12ѽ<(Y5jn\+uFPn{G ?0jM>;k`*Pu@#ZF=M)8jwCd=eU6>pD<}9CDh95ϱ얏df dP8by <8qGyf?lkM( ,tJ-IDfZ$E.%9ۧA0\?,L=m&E}lxf*g}*:ǣt$4rȳ#o^=ƕ_+ͨ-yeq{l^tȻ(mhdmy+.T@qcrE#g.a;G(;.c$oQ5tʺ IC<8g[An^xiSA%b~[]lKm} 5Жu'%P-y䶧=e4O6>ɿ-|s~<첳]W'8g"TD4i$ 2j5PIhh*,k[#վB'>ɼ:N1'9E%'} f KƂ5lZM71 y,3q&BLdxpUz*m3Wg2Ai|\Rv58=֥6v”WEX|_aQ/%R:cGP5d Jޏ:έ ā e x+HDý%O.;yR߾Qa Fʌ`ML߂b ’2W ‹J.~s-UKZuW\?"We碞Ycv[#!BĹ6P ?N$_ `%tKJ @-'I2iE`f.Y&HZ<#i۬J]Ef~pxcDrɚF0>w1CtNj5E/UF2BUc{z!#UJR(Cu@Ȳ2Px֍ PpsMZ(HyC\F)$!m:}܉ڗ?,<0cR8|R[CH]i G|V}}]+^ntA vlO0}Rmew sUn`y [M'ݍ-Y.*^A5] _sini> ?~]sRog;v9ۘ١n9pfxCyf[ =:5/AYA;}Gq:oo+*_1R!jF6}aV1ӟ+ {kp+iJ aUjv'W6!ƄIi%,Hf; y&!!;ߢ1vkp(/h'{Mɢcp!,Ͽ*XL-=h 3<柤~=rDNDޣލ(Y&piJG~f>*گrJ? 7g$;&*Z9eqWs>~ ʚ)/'e_1- iYLNayQiQS8W<ˇpy(q:24-l)T@A*'pئa  ?^ą`hcCn~rS n4ce yh'yg_t:e_U#N5U3@HHAqY2WYY ~gV)aeIvYxs;w >1Un}}vtWuE]Hos^~To]wojg`ki}'+ϱ,٭sFF֜AI ]︰yxu'3ɽ񤚤뽣 wMHc㈻wn|*2Qx/pbY ii!\jޠ9S2;ZJqγDBa4vLU}RHC$P-dǼgbjdM X}ҧt3u3!Ho+FCTBpTB:VIZ@N}D"4#@,VZ zHf  {S5wP׵R$RX8m򦻧P![u|!' /ϞtPQJh7"#L`>lF9뤊Cޏ2k++r, w4V"( /6yeS L>t1">}8#4-n3k|p[532mŋ92znpP9"rN mp%{ٮ?UsL=PڒB,ɞZSIЪ%Yq*V& 6-x^`D$َRs"S6ބ8fc dAaW4~!/0S%4whڃ]LSGRQVߍl?,w}6VrcbeGGYڥ2+m`[kqVIZ@< !O%9![:z(:[RԝC{,cmRUҗ[F4ZRޘCݦ1zl"{R\ U&[c0Oe |rs/NbsxS˃")ByKp%J g$kY_Q]z$R0 7dfm!q8GC0:\*\j]}RDS8ì8E[Cju{idl_IZSt̒5UH("`-L,h9܏O\tb1Wm ]Ay..hC wܲl՗ }!g\YQdǼ_ȀdplA6s*K}O4Ch_kϏu[$oP_|?7##/dʥ@Gu3-;;-nJ3\?6~lܱ/*~5.=>m/u UH(lFݣIu7TzsM @Dϫ7{ܽ3%$Hf}L|beHT)|G@PDq9sqR3p-ᔣQ!tf*y$?e  qNfЀc}xbHPcGq의2/geJXiVZeP>j,y}^ (y”,CT&&)Z"ݤ0̈́fMIJA,Kwd[dήck>jgh1~HDxǪI,j@/XN߄$7} H}j~;Jv 4 ̾Z$A<x@022j$!_Dvsf4Ɵ٭ײTوzx/8U`ERHz0@$] />'V̿ MiFo2)Emc/֒LѢYm~ |y/F*5\F]a}f>w&iDѽ).5$&X~'t O.Xc;B%R UT9qEdhslc{OCk_PcB5IC)G88V;H*b)iAj%ܰg,kJ2\ӱgJzEmV3W|/m&: 0*oǟg(@^]^ \QA!\] 3bZK[h> /`v3gO9O^ReO?V@g]zӡE/5}\ss.r‰{,*LkOUb:nͬXu) ? мdOD$%B]T ^Ȕ52ddzTφ,@L^ T2%SL8Z_fĒXsHŕoJ T"hJ1/svbѓ&8*%qCAt҃[@#]3vٷ}_cҗL jTWz.$bݨKyҎ-DHJZyđ> 1M^@X?~>lZJ5|R{6apRQgy7I_Ȅ`s@]E88"tCQzU+\dRn"$[1.[SNt~JxBMS!N{v%2%*u%Y~ TPI Tml/DhדQ8WM~jA>:;?M eػ"+Cj)Q0B/PO]\<1~%\$YduUJ3<iS+[:@$ڌzP07EK[1uy2[&ā-M."?ur͔sef*%rYYe[F$?lCLҹ3I*ۗpL0@:L`Z׋3°[fof%?<*Zi v%uyﱄ'jZ8iW'2>1%ɱEJb5[Jo Uo-AS'a36(%r 7Y0x" 6X^tZG endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 811 /Length 3415 /Filter /FlateDecode >> stream x[[s6~ׯc;& 466hJ+Q_(-Iζ319߹$LKf9L32`R0,Z3iXr&sHϼAg`K *8))K8i,<ѯ@Xl @=b)sh{[6f~fhgSZÔ$Y˱% :WAV|XJC f P%x@ô@RцrQfPIK(Cda ϙX/ z$`CxUG;v`)`@eV0!(p氌0-La@k R ݄\>H#ē`&wQ R:(#gr3%1ĝz4q's]0eiac@b4~p6pO,M;8H b|NCJj6r7?gAf{:_5o1?-N?M9m,݀?+b6,爅UpP0x3̃?&PZ2R~/N~XMlTJϏ[$ٰao}Qzlmָ̛9U#nU %3AAM\͂wKo_ 32Su+C[[ sb 3D'')vabPmyt1o?"fvS@n* * uQ9 dkV]'* k`u, y:nx9 >e& -AͶuҵul8jICY=c tV${Q^4`#i|_̥ԆOst#< 7 _2'.Ƴ P Q,(/+ݹ//9Anh؋c{0+Ϫ=Y4f/j4Z L kԵA)q11\/(jX wa=t)tJwJo!aJKij_WӃZ ߫޿/a2 O[M9.78b>|^Ypє|X͆qyj<*)?b8qj3ll=am>GEyd\,^W>,}77؞VU:hB/q~k7꿦;UŶNŜߚOr^zi K@p!?*b 9qpsx˒訽F)A ls41/ɥf`O|;;QqAEAńs*TTF﨣jbmop;Rȗą~ih\;K&sd̤"U8SO̖(x]iu |r T՚V@C\{5kVQgC|l{$lc fR<2Doi2n!肃 JE9rSZe^-8C.WP=Ю`)M{,rleXZD3Qc=a5R]!`MIٻ'W1ǹ}FCx˃_G>ޖ{|8ΛzG^gJվ?.N$ʻq}A#-]ɐ:~ӣ7aq}YMZe@)`z:.M9yE+NBbFOvw!W'|G)OC*:Hq3> _ _aQ)_\ ̏y#q1ۂ.uz##V->&Z19wG1ŸE P q=E9-l¿hԋ`~sR~ H;D׍$W uHv[$>HRr(y2zȤ]W?~ ׆l_K%s+mXsWJ}^C>hHA ibf[jۢSR(E(0zVbso)x>8: C 'χ{_~؁0.rUvņ啷Sxez87G/^eclӴ'A,J1G)GbS@―+s<OoqjG'8 "%W~_ z^Bwm6=l;<Rv;ɷm5?KlސgL5!1_Wl/FG7&kqcC4)6ESV-cMm}_4D ڎmoS4)[Qs hS2w 4U2u mܛ]m`!!|ZfrC6}I8b6Of :u2wc5/_ ۫s=FExܫKtǡ<1ɢWS qB.Fg[vX^T?{4tSծm1}keY#||gSs> endobj 124 0 obj << /Type /ObjStm /N 5 /First 38 /Length 270 /Filter /FlateDecode >> stream xڍQj0+Xy7@in}HEگJCA,;ڙfQ& 0#sUNTKH1a5#] ֫Cq9Ŭ/8iG}W K+v`޴]cz.%j!K$؇Ϫ1_:tu'pH\1wD4{sVDs" ĭk&)-AOgۘ!2HH̭ٷnl먧a ̾x endstream endobj 130 0 obj << /Type /XRef /Index [0 131] /Size 131 /W [1 3 1] /Root 128 0 R /Info 129 0 R /ID [<8D9C6480C02AA05482C1EBCD93662523> <8D9C6480C02AA05482C1EBCD93662523>] /Length 325 /Filter /FlateDecode >> stream x7Kas؍^b{Dc?npWATp5>p˻?G:*$6lAl&$C BC! ar!KkfwY*<|(P9Z*V+J ʡBeDTm UNo6B BCu$yYj/A ڠ]:zT^m:*QA@N`FFa `& S00VTU,-SfiY K+|XZ8S8/,c\.\ztR}C1 endstream endobj startxref 212594 %%EOF shazam/inst/doc/Shmulate-Vignette.pdf0000644000176200001440000052047414071641064017336 0ustar liggesusers%PDF-1.5 % 7 0 obj << /Length 2314 /Filter /FlateDecode >> stream xYKoFϯ :&9=8FIfl`me3hE*5w&1 E&&ُnOn3o|"b&ѓDX'̗Ulv:Fz*m+3e"}o{]m,5FM8gZLf*f<Ǐ*X3w8j+a`R) 3xB[h5)[$3-͹Q 'b<SVOfc8QG#jw:B>e:e{|b+"p?tEKfM~ -HSWU={&GsH& z6b蜳;%gJj;ۃ̸u !$! 0sS2+ l:v[(= oѦco|Io/o&VZ*GSw s:%%->qI,ͤFHC!j.NDѷc JQ@\2㡤Ή&0" T8߁ HTG,QR c2zک m<项dGwnz'p<1{,ʢ-հ~5 P>?B ON$.mSNy  C]h F|j2/Ա5 dظu<5&ʊ1+}&Oig!nLo rJDِ>IOr 0uM޽\>Jޒrvh, >cv1_Oy|VP'!E9c8h(&LJQ#g{Ta,`,G2q% X0w0!o t TEnӫ_a3@|Sf|aAs| fFs0r 4C(q!)dP5QZ ?Gsw0L$ry=pەzrHicV!5d鮦MD6;*-u>mU|U:*e:$u*o`12jQ5S%g BU]eż䭕@ ?m_¸IwPf-ۯ]P}jW6>y-dض yA^ 롻-)rʈ`SZJ@^bm'!^ߎX^k,;ZhkЮ:8!%B*s{6fn2kPYXҍ%ѾJs<7ZT2te |%FG?C~(s hW@k2k*MʛURɓWˋ/^WJ׵IVaXRQHN }v`@xt{@Gm O v ,Y?3r~5f~3/jÛKo~MMT 4Y ?q|?;# endstream endobj 24 0 obj << /Length 1876 /Filter /FlateDecode >> stream xXo6_at/ ШH}%J&(HB[-6wǣlIq; > ,H;޸zzmet&>xu=쐅|f޵U6[NJWW<]le~KOe`I2ZM T&iE3#)ƪZ-S5=6,c=Ʋ,x,;vaP[UNȜ+>1R#']Dhy-%.ymҎ[;_JbҢh&6z Lr 427bЏqDAG1 ?~R1qtc?t@;_.((t5pQc9(A|9gxF1Dq??sh)}f=œ,w>_^Tq9u_'riTRȪ/d_K_H]שq7"P[T‡*9䖁=&4a:pXєP$ ?冝ǫoMCHis!z.{ ֏U XP+T}0 Ȱjh4cn$%9}gbY҆P!At& o12aK 1>!ܕ !`k6[22-;K.HhrEFLۅKj^nK:k,jv̭&ԈXۇ5ô64kC B;|l[ \:H>?<RZ|:D5?`F P0L ZUġѼ(Ty 67=زd+, "':6% m毱u>mBG.k@GJ ~轧߲U.iA;3,UAoYLEl̵)[cU{O7lo16U03P*OX1P+|Ll~M{6}r@t&d\ l):/@m I?}Ah8W>D V^677; a&tU$w>\ta S0w <=v ':nѹ7TU ?a3{)FHVn^AT1%k,ئܿ=R؁Ki6,nS[Ct] S\oa_N|N'F:\:$7--&L|6:M`1ƚܻ-4)zcS݇vʺ))ԔQ|``͏ -Zß:5iS+77ό3u]93fNϡ^e6ro`niղ;Znȸ6֨`Ը[bP[.%~ث{7ZN"~ #.2*1Vw؃qXy*=RN UØŶ1חх{u&f_~}H nkml%ڲn>^`Ddɶ,׿e ⦬A vE?j .D,$5Eyڲӏ-.Λ\B-Kޙ|-;i'R;CE W\PQ\ $!Om4]8J;a(Ąm +njY|F%y O課T5D5/5f87CJnY\S- endstream endobj 30 0 obj << /Length 1515 /Filter /FlateDecode >> stream xXmo6_!d 6Knں C[Ddɕ&;([T켭y{xG:+:5;;&IfFĞӄ6gWYE>-zzn(hf%nイ(Hַd[%_ɤ2;>G~@Bx ]TՈ" 1q!憣=Ec6Dk6Vļa7|-t4n\V*LHU+8})/Y~1LRy=*<Ćf?r翨g:.uhTBv&W҅.uWլQ{IW؄ƪ3#[Smba2xJ(U81Ư̜hڡVtFZK1!9%>t>('qj(ؒO 3?)$A=Ɂ TM LͱaƋBYsRg Ht(HȒ "6@|¢FkjX*:YOzCM{6!I+. ӞEEmQI}v}k;Tkٮ!\[([ =0auBԛB⬫ +8o,6W3ɠIɇx#~g8蜭#~hp^Ͽ}:ڬ7eS(E*_#Du< }9%˄}#T~K8.̩;F3u$ ъi4+>ݍN&s\t1?8?_ts tHAQ70/g"ķ@u endstream endobj 27 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 1048 /Filter /FlateDecode >> stream xVKo7 ϯѾm#m5bKZ b,Ew=sSG!:zwv t솴5/_]pp{X ܇c%OSܣR<w $#S=bA 5xw/ ();*;Z2Ri $c{Xwq]}H38L]zApy^q%p61zUبQ-`_:oC2dbj#[%?yX}Es!{44N-؛Goa`M52){,#JmkgMw)LIPݚTqIҢxN Z*z S0A/&zuҧcMD'^ꉹ=?'zueM޶=Fz/5 =|kҠW(E:Vc6eM6* avDh\^|K6ۥA/(M {zf՜ Iy3%Q8: *zo.iB̪`844#gdaDju3SF*i *.û]߱]]0VӠZg-lVpu dm @fLQfjFBnG؆i4jy [ج6)6{ _PZ|qd;??{j-O }&rtqo/Ow^zxG~_x&wBЌz4<-?;4-~\??7662'7g§ ޾85330.?}nWC_k٤rupuZa)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 1426 /Filter /FlateDecode >> stream xX[o6~0Z טMmh*KB[d~T,j{ ]p9x@-} ِQш aȇ!0[ >}{ܐEBūۙ὏,Ix:x6&q2ER`' Fc) 0f2rŦߺXq+Np@g18N aIkrc]vS],9{5 .biӞYw*5;zTarCYI| qٌB2/ M4hC:Wإt$T%xed: {=0Uc卡ziۤxv!2BP,m g*Dv fEAZZjZYBuZkC , ntjeGc)3Y-2#l=][Vhژ2лYe}?d0&Vxɪ= 5Rt!#9*-RcafX<Kg\"ID(\VO*[} U©X-lά^:vɳֆBUw;}VZ[?>L8[sF8ԛOB;HIdF|Hk?XִF5R(oEZlrZ &NdaED"pJ_WNz -yg6K4[FHϥ!PcE*6v)Bw\jI+.ͨXb 傀FrSMsJom,$TD; g=l Zi`8uNkɀH߶74GrWB8L *Ho{p vv`9wYP@a:EАU]/>S=_€_}%WkTa"y@%yJc8'}@N%"hX]\xg9jK{0ׅ O.72`O c].>F6_Ͱ4niZ1ﮰ-HW,t\2;]M+ڼrit8L'TT#ҺzvQFp"~,)cо~_Jh ;B&/}a]VfurۜTJ=XZ,I##\w8'kqF}#yzr2W { y/Ubu@&AH/,e/98 $k@vDN0>ͬm{o{kq;W6H}uоW._$hTP|pA(i 5\du_$gpzwsz M察X!Y[3."2H+d endstream endobj 37 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 1048 /Filter /FlateDecode >> stream xVKo7 ϯѾm#m5bKZ b,Ew=sSG!:zwv t솴5/_]pp{X ܇c%OSܣR<w $#S=bA 5xw/ ();*;Z2Ri $c{Xwq]}H38L]zApy^q%p61zUبQ-`_:oC2dbj#[%?yX}Es!{44N-؛Goa`M52){,#JmkgMw)LIPݚTqIҢxN Z*z S0A/&zuҧcMD'^ꉹ=?'zueM޶=Fz/5 =|kҠW(E:Vc6eM6* avDh\^|K6ۥA/(M {zf՜ Iy3%Q8: *zo.iB̪`844#gdaDju3SF*i *.û]߱]]0VӠZg-lVpu dm @fLQfjFBnG؆i4jy [ج6)6{ _PZ|qd;??{j-O }&rtqo/Ow^zxG~_x&wBЌz4<-?;4-~\??7662'7g§ ޾85330.?}nWC_k٤rupuZa)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 255 /Filter /FlateDecode >> stream xڥKj0нO!&YXײaPSBKv *N]Ѐ4da!qGŶIs90qvejSZezpnGB7؀@B"a B1F(!v3Rsk|nܒphLRxnZYvɲ>o @?VdUUw{,Mݡ)S&4P̟K.UV՟}On[SiɕӬsY(6[P 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 1896 /Length2 20360 /Length3 0 /Length 21507 /Filter /FlateDecode >> stream xڴct]7v&mmfc1vvcAM󹿾k9=kΡ QRe1s0J:ػ202UYXTnVFffv8 1g+j P4u`f恣HB3@jdPE(92|V@1G/g+ K?>xc-56qpۛd L+=hilkp0*)Eu%UǪnELUM] .&jU?[>|(1PQV`a b'a@Ssg-]]y<<<-\\-m§fipp|\foNWKT ge w1t[h?}$Oۿ.@4vVNII`gle 77Pt5vusxͨ9;!?l}=bn.\s+[.jfeO^DAFRBUA>c"rnfN ;I%>PIG\ƶws+{3?7ssdRrrʈ _< +t=M-_Y?07uY?.p>.@Ip̬L]?Zc\.cokFcNmf@s8&׏g++YzhVpp3/'L͗q5{ [GYb)ۏl_.}=B?xL2 b߶KO 0vv6cVGc=j ` \&?(n?)fο(.?`L%W$9>(/GKi|0do,X烲tr0.@ |~_>>{g@[_ PcT]6V.5{k&?- zMVLBӛ+%g*!)xϫd Vf:q HB%Aٔ%rҼiAeZRk͔$D5ՃW+IiNd󋴿e'Ox:{XQo?UoBz.`8ۢyut?c$cB9UX b"F׎3C_3Wˇe_F a{BGH-cp'-2BĊ=j/FZδ uLVql(b +dHCh+3 CȄFDtI'#VSH>d)J9/yeE &0^yM۔-f `[U7RX!n %n=N*%J/ȢHn&ў+d}൘ϯB(N[NuϘ ԍ5DždmC6 vV2j'ڊ7h5oV7@(;>2gbi#e|FrJ?51[bSvE#ϫD![T-4ɩ&gJv1_z9Њp^}GהݮLG>ch@ ]"*=ĪwUe3v [tdB=XB^6ڣұivSV+Ж I-3*;i@){WХ]Q۠RuLO n8=߀Y[X5*By2ױ1%BK)IM6ga%sf-^Bdeve7ͨ͸~ ZhdYA(%d Vz|G5|*6Ζ P#mƃ<<\cdIhݦqjƾw[9ňLcJ.<*p ӈگENn^ & ؇=~//1U>!A-tWMV8⾳#pJ3#x=L {J5wy`J"Zät(`SP=a.ֈCز RE1ٮ@pX#!?ӆ }{zAOrD  @3W~gEWLvmo8XNIP7ϺnMdAĈNH@k{[*%ŌoqΖ,rVSu.D`IzZbhU[3 &U8VQPK4⏱U.@aT4K(dc~V&JrBqmIw!Iz$&7&`ݝ$/Gzh>T㛎 1yƀڐRn I +,zW);Nt^27-ڧ摡fYdvP7g.L= x%BjE487-c?n@9ad¾Gz0^Lz<:U0>pQtrj,sBsU m MF}e|d-'x֮,`;ԹK۩4}?)hndLK..r kTc˹Ipʽ7pLfEq)FΒۅ!9(0u}aVLhCFI sq8HQ݃vcpJunf(\&H.ga_YaM%(jsW˱]tXf䉼n[N#yKSzfuH(J0\,_yȠX; ܯd19DJR}(ڌU~-exX,Mv[c?~3s˖4eqª xˁ]l1̻_?xZ'1QٞvTm$X PbL @ TE[;q/JNscW<;A0jlɄ@o%tk?!b`!a5uWj$x\%fvF#PEbIIkJAU+~Pf2.u\](Q9oW*iᾗD_q"z_v=@D|w# 9Wi>W;12mOdԆ":.sm$-SaM GGhUUy7%ڱ+]su^T#-lykΐS&AM\ۣEs6TR%^Gs6juJrY:BA%.sЌI ~u'LOpYlˈp BD{| 8mx掗stÂ!3aj\"d5#/rmK=nH500"A@&xUl#i ;,x־GZfhY1W*x20RҾo͍`%eX(3b}! D&DO>o`ە~-)\-M3$pqhz) _ÃG&oX^Te50?hf͙:nNz:\p)L~kd"?ykVg:t-ӿ M݋}zW~.H8q2`),hHWS;)i"2%3jZQc\f+]AˍJ"ɤŜn썘-2suꬢs /D+$@ iwtYMJ>h0q-]{y%-fYڄOR 5Hf^1 ;T:`AUܣLM@$1j<-~!05GѤǬB=lnz>#9ކթ {= U, <c&a&6M|oxYp^ ԰7Pi3{OjQ.Gi;<>mn( i&L9L_ܪN~f$ХfVȘ }#\pȯ]6-s:Vz%fbxp'jas~'%UBbqu`Sdl'7hJwB[jr)g > Br75 9&B4D!ÁmuC%Q[d"%|@ly[H53 ʋٓbBaL jy>M/dhoRX:F^U1c+'%$}F >E_tA<ݷDs53.yHጜ˰F,C҈.Ӣ̠CLj4eLO3N%fѧ'cβ6a,}~WĈKC~l陮e4b*`+޿{.~s y,ϧqG|H`1zQ.[[Y v-4q]ů:bK;T "P]vN#0A^ސb:$6O;mZ cK~Tۥ1|x}8d(oІWTtnҥA뽾ipքo&,jl9PAw^V4];0x*A[oTMuBH%JNȟoyT>7.^ri%(yszѱTdv',_I#>F:e \sbWo veV&H/$^W8ky?Kx KץooԽect^-,9N@ c Z|hxueU\FgϷ'G&VħS1D0mHKp*ˮFΉdw7D L3Xͯ;phf}q;[>qtJ+6:WA$ۘ`n ݘ&~o*Gp| _~_8yrz;`MmKE1J֏]0Q;-p/IM =`pPUj3!ͬ !v*0UcMdFb_Ѝ~;چMB bh%p} NWV8?`6xꏳmݪ!iI|Né˷<1.aUJ ˼?Qk*3Б],kn򔱪dYP LQ.9,dSOW6Pw{Rȉh17 -=c7?ߑ;= ˡ(ys PV̱ds-H""5Wl@7TSyEYAa0*$7$K`F\9Fslx/5i)L m*~2󧞑sߴ}7]Zn ZF&fM6g =0$@: ҚQ lyBVǽ3>U$joY9vTVVk4|*PIVF ,&X{zϬ > Xa=u쉆V$0`|Dz}F s|h(^S[VW=e ߹- -LtfZ,xbVrPї{/h8zMae߁d lcijc6hxc.1H grte|$OT j uDc &mI;_Ӯrd4Ai~Q7Gx#p=ltt=N>701f$tߋ13?oNEKgTqf mWН0?lCEKX;N{.p&`iZæ'|fϝØ\xTt,VăhB65_uNH3́Vu-}c'EFh *b'\$'lK ͌ a Y`LĤqc&'h[%lvq#ϻp.-@['r~ܑTOb7'4B#8@'<GB8I`Xx  \'|"f^R>JNz.U ASR#cb۱[}/6R~ 0aDfMB $Rޙ.kwZ"d$Sr@uR&] S΂B @˭:eP^%E',t;L0`ynZhD1(;.$rbPoÓB.Xys5N = ˜鉢 ! #]- xWF 8xI'*gF/Z;|>ݪ$%JH8*4ns^öd&[<&2_3}=Kܨ3ֽz7TColѧ&C3S,))[Tr3_!UlSOȓ4o_&"9I~eGm q_a?~ou4spX_mHCUr2`XA7x!Yfjf9w8CgzZz\3-h:̡ZzȻܨͶ+LzkV؎}J~h[pX6.p'%EX"31M\\Xz.9~{;Ad1E IVc-XدT଻5a }YHYF㘇HUI)5u9[ِ۹ܱj ܮFC"\U$ǂ!LepŅr2&W}1w_a_o3oxAŊ%TQ|DP9j$ٷc{KQuo߹ia -4g-jjǏԩ'|CNB9tDXq&MgME =AE̛O7)SNBܩuΛ{4É -.f%bIX=! lTq%AA)'?/? ez?5h"UR{R؝XOn}DC+k5Wk/:q&~t .6RHzɰUNa' nmbmlN4bOܵWQQ+%BN=5G{en*a{CԮSzXMx&U(z9n[^‰{"_tח%aXvGR(LYݮy'&_Uw{SyO8,f?kN*hc =ʁ*:>xM87 H2˄{)`7.dv)%_"8(sp[ON.KL,Spݯ!Y8'*{R5YS9kZWXp; &9yڞmm_}Y h%%Y_ PWrm"sXmvQYLП׉kfBAm)u} gǚ ==e/[ױ9$ͼraY!UT jnCՙTG`@'CH`T 5xş`'4Mw6A.'s-t 0?<@émS%HT]׆X/i+`fj}@1nL@v/OBu]VGi,T$@m 43C ePtS$P v &ɪ9oM!KTLr;Ԩqꗕաn0_9KFg-7w T|( 2G?+۟ /#|~aK'~NGI{Ia4=.Z#S^+<6rwZeÃE/L%/մUQQLiP-#d}o`a'O$r=@aϘ*k'\b5A Q+bԫP?بT x*`{Aܢ7DM^wߙy3PBAD=&1.ݬ??Y@rc)p|h?YDW8Սԝ] >B cmїz Qa Ėh7P$T!oh5c㌊Ƨ WJO- ل) =PwL`;qe5Cq.5Ii}%(pveZNA9#.83]twD1SZ={+YJv< ϔ(e؟y_c];e ߮&ꆕTU'8@t*Y%DDjK?jV2K5N=9\DLP7:\! X 80">"ר$ApF<b0=!Ze>E֨g15n..ZenⳢ$Uee9&wɭxa}ܦb c ˭Czv8mA[8✈\ݗ = x=i IhΞIBnބX'\=f.X/-SSIzb-,)pDV!vx*DyYDlxCOKyXB)m)cE.YE8þf*|y;zmӆϹR2bE|qM泒M#7{<زO{+"(*#x= ~2}|33gڃ90ɌIiVJ31rI'%bܽseBյ9|zۉЫُG: 9+}QVFz۽@=%H[7rSG`xϛҬ3@Di7Su_h“= \Q3K fSYju()k"0׊9Rm|mƆ-!hMءvlFvB?H/UHŝQu88ED $^UjpJeѳU%&.._$QG~c%sFuØ؜xSnZr%8)9KFyiM>gI-3.ss Gqzwni8ץF05x.N%iy3DB d=1|ϸL߽$mtKۗ!{h$/x4tgܛmlOͥ Hܩ Gŕ˭f<],U`춃{w8jSMd4m jRU~QGj]Ѡ_6?*|hݭ:I::]Ӵda:jHD %~cYKez!H fNr/Hઈrs3ma׸jQrWRAm?΄-BO!s /΄~Jd SB**~ weؙs$ k"-Kir71| ]${7a\Q6ϐ2h[A}n+b/&"p(Xra5|ߕdn\KTgR5}cEKIwVr^DіwQK*AG45 ~^HbQiqt:ZZ 5m&d%ߚvlkd6 E/8lQb õ4Z";|%Gt$L'hC_ *#H|~QlOaL`!+RQ-F:\O/Tr^x봭IbWUpLJo6Q4vT1dD %fh6T{Sm:@T0؉bΉ2ˇ%B~;! d;wX)C"?ox)FOrebĔ2%3J\ޣ#lrgtͱZQY5ѸA3:e]ɁTj\'fIĪ_c3DR>7r (,>-_Ju|xD2Kd+R,ZWL{kVD)zL)-=Q$"!^2U/=HM4#J;0$^#t~:M!^?# IC%?[3+:fY-RW-_U/Jϖmz?o,$ZK3l36>@G xЬ.ģ8Oef;i.c%X̾;(URYlV=V ~%/);MBreElV#""cK oDӖ._gVc3G+}Dsko(mU Oz=ë,c "{ܞ& D^2C.`zK*uG^+)ׯք t?jKFdzq Tdϖjr#~UǢQQKw~2u6Fo%/)~t0t'13y9bv&6p$(CM)=406~Ak"*A>Èa#x>=}/WdtnLw#.hAMҮG6'6'ƅy&q(?e߾Ob!W-jטEyC_;N}HS\QlHW`YB~wv@۬vs_;އ1DK;JKئQXC,0rD:9 r72B8$z*rAZt,Th, xrfA%5e?Xy懒AZLŲ$knZA0[cWx)3dv_Vzf ;|T_ۣ2( l{uݦ]q7 WCǾ0ql;)β0 SXOzR(g_bV؛EW,!%&8L Z3*\1œ;uuxjbn "PJl;tn,CL` ϦVJ\4SC?h yKz6>]H 8]9;,](^0Yq *VydMI9 ]'rqlfa_DA>mx8Q࿆ܱ2ɼ}/S!̆,El<TD6oHʾ]q>wXpB~s db"ݡnv++T {ځۅAOr0j,tgRԉX3~B`}Ff1* ¶\]CdkI!>ۦz7шa⸅jRQ<X(T`̣PIE{IXcE 9ص=B#x%>~H;Do^8æZ& ؆T D*jL(7iX*6Ŋ(vniJ`ba]*>#?osh"}]o=+0O`|VeMuMV¾+crƜ:PY+p>RFlz pB&LVb'Ju_L\9&G?̆o[,Wj^V \'{{ 5,`K2,aX,E@-B 祫'>ijx}r4yl9scѼ+G)!l e.t8f^htCAwl;?Ou GphvQA[R 3QIn݊}&J kF[Vh&Ӫ%ߊ7X~^btbjeFnWXKCje )b\J!ggCtfȭl3İm|OwzI'A5C ΋3Y#hi{"P%=N[O?\鸏l`T'xk %r~p7=h;"ăE [ Q!IK\R?-.3Ó#wfbs ڈ:YYU5>" pw z}NwJ Cwnzh)]G*S.pbQAw WuD C,AS^POD 2|R*VVdzdn P~.;DD v5 ,ۧ M$sD*6(_k Ŭ)(w$cDWŃXO޳.S֐c!G{ șm x,yRp\F9@ v5sZ&*(9Q _F|ldP!(tkdd]!'qgƦHv[߀|֮vYd }),3~/ N:0>m[B+(<H1 iIߜTihԒqYhد54fyRp͋ZN!O!>1$,Z0}>Y^RWOm KՂ#|38n_t)Nz[Cߡۡ_u-q /"f/ΑvO3Ha_'pno.,#u~F#dppi.j#m F_TŦ<~^5VuFDu'=:a/# <idok6i>< ~=hNNfJ xmҶOm^$3"oXBhYMm tmgy-FY|*FM6~ۂ ^`vK/t ϏfϪX%0x!~Qj@?_)# _|0#yNMqN)_7k6çdI(\À9dڕOT64k2?]da&A {'#N)م8[q~Š"k(}k'mޮմ 0\~/*LU<ϽaPI<<PGvbo_gun__jRS(KA|}ϱΧV*Ad'^ RV"r *ce3l4 EqI9t]Gj[.ܙqF'hǹVET)1J[UsĜj!\LrbY`/8%' ]aB2>73>wMИ 0 p3O״F{ Z*Vd|O};e=I?.ȀB XG:3>`?6ߢ!nt<׼ZO ̢~ߦE. d('H׈wtCQVO+894(M,<pvn93gLy# |њ԰KL_Ȍ0:Eva&r-V #E`+/ Ld q85t o[8RP@Kq1YPI2Rʪ#WoRHVtI $۱ u4vͲdh*%ˍBiIIWP3 @_wDI v~lcq"E`^P<^Hߵ\0R1g)!LI,Nf-Q^vZam.u?݄`,R,N f/[(h}/n!I >pUw(<1aޅ&tMtXJ;B B2契2MBC[JvUl/EGP8< EjVEaӬ=kl0it2ctđG@vS-?=޶4~"` (س^治 `NQK9y`mT7bd#0xDZ ִqŵ>)(-e!<ٛ267g~Df3tJj닱g@^]DSX*8u4Vdy'/Ԋߥ"AϐH*`W$@Fv;/x0(''7i SQ8%d߼CcGv ݜl+CUmv 5fϠALR֚֐ zo*j7\I B)yLb"fZ-YGc&&zScAǣ2 u5en g;q>ȒB lZѻj0}.;erIj 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 84 0 obj << /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)/Producer(pdfTeX-1.40.20)/Keywords() /CreationDate (D:20210708112140-07'00') /ModDate (D:20210708112140-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) kpathsea version 6.3.1) >> endobj 2 0 obj << /Type /ObjStm /N 67 /First 511 /Length 2928 /Filter /FlateDecode >> stream xZks۶_ѽ#Mw:IMv&Dl$!6g">fbY)`i 2˔KgF*2cCxt:fb30)Ôe^&L%[̧hxJ*4Lk(4ih-~i`3is74H 4 Sh)~ϔF$#L ?Ӱrjbr =dϴHo0'a ZQ5H`R /j#H Uw*2g! }1'`ůrdSN9yM [g>!PK vJ*BX*ALP0hRR`085~}|Q2~4pIќF*giG\7#?rS y+߱3q:axqdv^r|6Afğo^7a~3jWA8L fְ3連¢,AޤSc\Q1UcrZ4qD>5S}o.$~嬛zrY.Ht}ux[SE*qSfN@ lV):oHiiں`[WDRPSU9{ipfB{Uf Gv= 1{S+CaD<#RgMK %^J%C[9?h9eVe1ٓMsifWkbj\%5tF9⏳9rV^̸i#zV΋nQKgAyj/]|QeCˬkܬYY-MEYFl8I _=y+-4 ΚkYΚq8?}{gq<Φ5H9qSӝ_K~B\hI>:_u! w,2 \ i cl!41R nBR$"ԭV$cl5Gom΀`z3$XD=`k([EkD 2-&=m->N- 2Ii{?ޖ4V8gUqZ|G>~ݗT{res|'OOR;:i|!{]}&LquɖzW?)v# ,qU~<3/-xGN KF6o^v\T[t+J29neRSs|6d]N>e+S LV7Ef$)bjQMtͨǎYr s$_v-)oJi: XoV.WMCexgeosyc~^s ivu>=i>31/rZgw[p r{8 h=g;wllIؚӨ{ Rġf O!2 P6x]fv@Xo Ilt;G,umTc ] /Length 226 /Filter /FlateDecode >> stream x%I.Qs(ʧ+])}>7U301H L$aj`%__}˕$%9> stream xYY6~ϯR5Cg< -PgM-im!J&_e^7N[Eɹ8~Csoq=~{>1" KO bxQ2C<^}7</z2U\uWv_/4 %,қ$6k k%pvbkT ߖ{"(T8J.ŔS DTG,@0 ZW XE R |TxSMǴGZWhrW>A \#罝@pXvtm*[Kl"|Lp͸sl :+ ;\wB aWSR~A; =C/ȒembAca*'1ziFЛKʺ!?̉^!e0 bUf3fmP*FL_KR!IhAbq |LI ` ICjtN A +x,pKʼؿixL cB*BŮVͼ+ګ"i䇲*O&B2.fģ?"`EE0c$ qz1Aľm*", c&S$f%$!GM9QB RԘcb:𣾶 4)͜fC۱G9 ((mHܐ6pSzmtVlag @E[R2 ~D[!XVyw߂*iWu;qٯ3G?Yk22+1Od E=BZt ;;R!tm l&H(!KaJxsɬ&RgN:J[vPwCӠ?[Doil))@6CllS4;hm΍o; ) Z6q _0<pEC`{Y lٌZB"66d^oU͖oOW=l zx~׋/ !T֋` 9$u0zJ 4Ue@ifbPUngY̚j;{"G(,F.1YcQCϫ;_KX0;)"( `pE 869MhWUY^+ݽ'L%gŸkK/W)dXӜz,(97i~+ޡnZԪrz˘DY{w{b^؟DOS'^؇-sacM7xZm:2?ˆIef (ri*C\zb@'χ7a;~OO]i7渷"4ͪeKuu|tgڀlߺ{pz2[`|~I/Ċ,u ̦XTDBO) C2Wc%T[E6,kի;4a <` y~x3۳1jRudmrTKۧmnnQBH =W(G/;ĿA npk0BB endstream endobj 29 0 obj << /Length 1303 /Filter /FlateDecode >> stream xXkoH_R!J#UE#;h1 @pqm7_< swSgl Ng``b@sG b013>˦gBwķ`6ˇeqjA| _ĒkssܛPxI&,D2{u^H`oBX3Pw*Hlɍa07q\5cdt늩ec3v|3x(Mr=dBwArv:BC&g3|kWh"#doӥ%bZT]]-plj'/_96"㢫x(uuʋ +:X;ay:BB^ ?Vފ |mꭙls淄:ebeZ7E4WtM7T so}`@5L ꃩo}GB@eJ%x3@o,ۣo'G^pCh_]=]]TQϏ$M'U.J2~ո{T, ZS+ XD6BIEVC$F6Y&1p)jɤ6 grɴrXht߰{wMa/o䮼/s=ՓUotwmH^!NTTLXw7yE%rHUu_-B< 3n#(K[y,-i+J˪dm@bæ&X~pۣM endstream endobj 34 0 obj << /Length 1197 /Filter /FlateDecode >> stream xW{o6ߟBP@bE$X%MGx؆u0h5Rȣbɵ:i0~wɵkfu:~$Jv)gO,:CwH̢:,`VΗe?=zRO}C IG74E {0D (sڻahw?>Eq\MVWJfT<^c\uˎpq11jƯ =}t#t.hER蚗"q%DdbL{_aݓ/+9N6dXEyd[[3Y1j {]%uݗXAo 2s6ϸTd|RB#m*jŋ.#7(B}F3b_ @٠M}y">UopezN@O:$`44#Dۓb϶>DQ'pաh , ǵK!2+|6VuVn%iX>,iȥ0'qKw3c-NIϋ3@'bn%wZC(! r^g0@:RLGT <S,/ۨm0g|Q>UBϾ^$@es /TC=4mк;z* !%hu>eС]pEm9ڤ<ڧx:ޟ՟027tx#_=m-, m<6x㱺0c7d{aК7A+R\{`[iPCCfy!_amY endstream endobj 31 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/shazam/vignettes/Mutation-Vignette_files/figure-latex/unnamed-chunk-4-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 37 0 R /BBox [0 0 540 324] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 38 0 R>> /ExtGState << >>/ColorSpace << /sRGB 39 0 R >>>> /Length 2766 /Filter /FlateDecode >> stream xZˎ߯襴H2rJY^8 0#Ŗz֝#bOU"~{mnwrbH/moN}קOw'f]g/ >ۿO|'='O>ɇio a0ֻCcbv qS~կtw҈| ;ԴaU ,=dU⃸2`a>ElDp<=lkA5=v5U;1鈁R"z Ȫ_ fգR }23\Ñ~,p-`#`r]v,߁p(} 2. 9ȶJ]AƑ!^kyT]yc= 12 9sU]AdRa`p Y_T.D^}ia/eŽN_`-F3WD/y`0\xޖnau 0Mc@V`Z *e6g.>I{":#EE)"rb9tIM G\xA^Eff3 /meBhE^t-/_Z}I4>՗>}!"ra/ȶrIj 9hG }APu zxVh,6g.q?@ ȸ0RuKM| QAqD+71l|)Ki֗\/#_PwR(/%[_J> ._Jq$T"JtR)zKA#գ|Jx Raz1+xfE&+|5Y!+HY 95YɊPLVȩ̊PLVkcqK kM "y SɌMAK|fޫ/!Vx=W4T./iūC9'}උJoO_^s+{vgޫ={ÜaQ5%}O?4%"|eOy^F0qEy^E0qD@]'`lrnPr K,~NWOd_Ġ._;ebS ջcJf<p=wꇗO?:TnBԀz MC}̠)Ļp[M w~d9^K@_]8eu"yQI2DIRjQ0Yю.@i xh6ZI;pbF |hyY Hgܠx;]#Z?"ZF M,MZ40*O3kMѪ?bh1hAE%׃F \M,r«D6ZEoδrzh+\5lh<5-VăF M,b {F L 9xVu-L-6ySZJ#<;3&q!qƪoEd[lh^Y^%,:ǃBs> 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 2521 /Filter /FlateDecode >> stream xڵZms6_M!2&_Lfl7N{%EHn}w EД(۽/b]3߶oϟ8fz|/#6:10H#ϗ06TH>.&\o'<'m5)i<]$ ܦ_+Hb!\Y|vѧ3ZUO OFex\[/euR%t1%I[浉L$,r_//2_QeԑD _\-aKx4^Ol|CUy+p'nJWiI0JP01<+ޘhʪLb;X&`Cw:|f|N&Kk+Y0 BERcDWJب2]aK"X /O .G ;g~w,$M_goϽ^u䩊NKFЫ"? eY,i8D*+/-- ,j􏝞2_yJ|uzta'q"<"+xbB32k:tk45 I 91ON\ȿruy`clAs1vVnLUO ɧlHt~P+՚_iE*E:>þI;б&-WICȥf!dT'$oJUO2SMIli kµ-:w7ur}]CFL=PfT"ɘh:6%!9,vPIAvv{eC8zQݦ_d^,/~рRC&}5[eRpS >;>KSib-pȖbMeIMƱ5 ~  SM-خE1*Mf\H<-ugofʽw:wj3sMcmg?M ]Adk`n ʵL`k0tUs.< &b;Xpq mperc>$ż|g5*Um|QY9`.HV^(xjHxZHN,ff8X fRj!;4z"X_ݶhDBYxL;r!3Ae㡸'rK4e!^prb-/Xs הNAlۊj#UўV@#wgvJ+}c: ܊S(bɭAU׹IeOn ;:fl6kܱ'~XhicE]X9Tġ|}q֞V ]O# KnN@ϟ-\L5Z/_k\J|/\CjZա݊4GwtۏͣS nfϓy;u.,+wl5_͜(3*d}ϴ+봪_*~[u(dcZtu¶J;pP9*a블SS#oj.t1ԘvΨY6/-5 4g̅yp0Oo}U\\̊jvkMUp? cCG9dpMlP6+A\R&O^W]LW^k}Ou(+;_wԃ#ŚXa3~^YzVϻ=@/ɘsUR.3?E˚Fb>E.]/I]w(QVѼo57e1M\j )zJzjlF9.'w#6ToFF?9jX%Y2_ef{:{jjVuY?s8qM~Ƣy_[$^n\:~#9*-mUzq%/CR5La0'z(q;f&`JX'fUu|(;[XtFguׯdCXom[ ˏO_>(ïpO_> stream xXmo:_2M"@n֮Yul^NȀC d@BLHմ|ys|#Pf @i"%Wף!(@"Ƃ &@'+O">Jj3 3Ė/gW9v {GhL}NBǝH)dJpz]y%g/dstNR 4Ƴ0zQ#2B=NÈÆ8#Nc ^ȖbfQ ڋfQѴ=;}ƒ,qQ@}q9ٟ`02j&02#G$OoOhn+O_0JC-J=HSInTy@XHbxPzD5uڄ[p hs7^|8Ӷ1sŬ]:ɓ&AJ(;$v,e]Bۙgb% 0J$M1tQXtD2szAײe>mkH}3J3.ĸuxx5v-O&$lgdUFTd]mO~2I;_#n1tV")z,r\6EIn) &R &ݾֹu>xO}ǫDZvudU5ڡ=HK7(U_yxD ~ðMp G_}Z{%rcJOW嫚"F,3eU 7 endstream endobj 53 0 obj << /Length 1372 /Filter /FlateDecode >> stream xڵWmo6_!d(@Jh&mdX%ҦTf~G)[%i"y$=wrs2esB^WE]޾8l 1yDze4iZ/#B֩<V WI_DV1 C:D Ns-<*(׋|wz/} m5"ADiaq &|+fAQckHv%QkFfy,RPŝZhʤs2Fs! .fSB]\Ro]^ w tkB' WMYܠNC'U/o<L  Kfi!aA*5O5ĜP$P`cbPniñFZ'o'8Wo_Y^_rvq,jV\Du:_=I` 0aRB+!\leu RNaB%}]Һ,,c3T-GElEµE$;-\c ܛ?9̃Uk̇B*)ӵx@jtkf*ђ2R:\)y+0"F8r,E(D> mH9afcf gFN8O_dv}v4[D~ޑdךJ՚<ϷfưD!V")U U?E\, +x@V"[TIaF+WゲGDzvÏ*߭z?dz{7?%^0Jۓ[ѥlЦ] jy\辧p'qYO^ߧ֒jP^c_ߍ*W/ O'rAܝ ;6777<7D7Kse)Ɓ+6WJnnzxRb3&FmkrϪ{,>%ϲe }kZة`؜o4> /ExtGState << >>/ColorSpace << /sRGB 58 0 R >>>> /Length 2397 /Filter /FlateDecode >> stream x[KϯQ:c$;(r0|p6+¬lke"MvF҇EXn/oůoOJ*U~Lqxw/뻝F%߻kV?~w NvsW#SVI ANVt3FX޶Ԭ;))HH^Z{'oz[cu ;]].ҥ.\$hE 2v;$+'0oYR`_)V=&y!Zz;B$-̖d05L2*4>aTrr$\cW'Zڑ."{-BZb)v}׻gĹTiodg w65`9X%LF!8Tdrs:G;#삠.J"S~x12¨QnYSӋK6[o[U[5ec">o뭖?F-I3 X/mlxEka}ɕmIʾZ4[S^,">/w\ZZZ?k_R-9v"S."SD'9LP Qb cҩ!!Qe&u&J7q IIbm)*D9 KiVL(kz ֌Q_a7b$yɢNһ̌+qu+橸z*/2+S#땑MP#ȌL/ s-\5=]sV~|;=[rԟ:T"%xV||'οhrJzɳPYpzQ|Y+W7w?yq:bjU :Gzۇ?;ͯݼcŎ-xdHEM!4Ȉ_x1Oɝ|) x7w}Օ2->t5JjL%!ZBౘW5}~$ihiE-BYT9Sħa1jhiYn+#A.'+kh,l̙т%Apn@ *xPh6RU:߀*.C@˕vΊ . hFʰh--)Z>G?UΌodJZ㭿p f-?X`hr "?-NB .]^| J;V ǹU\BR˓-Ǩ3aCB~ U١i{|Zp*.a㋼^jYZl;sRIM<P ?_] ,ɽ__ li#2%I ,+U S kv2Pḁ~W߉7>ۧxB?l\N/?|((|qxÝ>b~]Qλց䮇 p2P~% pDXG,rXQ#,#ZsMt.Э-q.?Ќ xv$ _Z^E/,ٖwroMzʫy#Z"u?dX+ْT| aTgEka=eZlZl]W_pXY @y)We)k'hls wZXOy!ﭖ@^mӱ+8^;r%[걼ڒױm9zǾށTL*jM'&W|y\2^ٞ6e/u8u8/FV!Շj#eA}DƒxҬ ]u7b]z<ڝ}vwHW´{O]:Fiw7=v'JSvrݹi*Jhw2Nhs\;,7ڽG{OeڽjvgV9iwi*WڽI{OeڽjvgV9iwi*Wڝ[v;LiwFk5hvڙvrݹi穴;LiwFk5Nhvҙvg$^#ڽʧ2^gݽ0N/Gsb;/Fs@[7&wp` endstream endobj 60 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 64 0 obj << /Length 708 /Filter /FlateDecode >> stream xŖ[O0+,xI4bl'viqT"7!Z.]2_?;vFR bUUs>sIHC =`Cb0ێK ѓu\F!2 B0=\A}g vY%kYxTR`@](``>` C%?oy̎yn;?2h\ gYYkA]j}Z-x1zX;$שP1֋wHD"B%~-D^iyF-/F47JѰӢ9LFN:h =;dL$3Ǖ KEf _g$񕒥(W'sYi}7M>ڢJl”rh,G$iNk-=(D夊⻝֛2 F"Hl0=IV q,|f$k ơYo2FIh*fד<"?m;BTXw.uGNn%Uu&^(`oT̹e< eQm=b]9˙LHLzt,sUn(|uZ<{+^-ϥ{-Dd2tE"<Ͻc:^{ϡ꼦  ecAx,F;=pXZ_ݨUq%Po'> /ExtGState << >>/ColorSpace << /sRGB 69 0 R >>>> /Length 1546 /Filter /FlateDecode >> stream xXKs7 19%ZXiLj!UyؙN}bג"9Z | V}^SFÏ˟>r'gju54J>VmGQ7|k o(<L}ÑAyƤ?2ƒoPZ%M E{F|*n: 6i* `ϥIB!GP`+k(1F[#aY(ȸ"~e+EU.m1$C&MEKu5=na^ PР XREiU J>&*M1ڻnkfiU#xʸWÙEWAWf3&8ZQ6V̏te>c !gOrx;=g'e: VېO]~^{FoXBpcb oY'm5g l[ɑ˥+]4#ʾfJ\(lq_ʯr?]P[ >D%E3ZP+c4"Gu-hUhőݢ?ň~V2=l馲T J.5=F˷@ (Q;E )q -Nu.`ۤO-oæ-:1aNi 6, 9sh}hy\-Ђ0tWh_#|9g]~4CnDXc̊Vk]C:1hTΒ ;({@JRP!b2.}kE6 |7W2Bv ma':{>鄣7ЍF> {r2Sz5R3 K/<ǿ׸_o^>ԃzzJ]<;?mn endstream endobj 71 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 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 2021 /Length2 22095 /Length3 0 /Length 23378 /Filter /FlateDecode >> stream xڴeT\۶5Sݝ.[p=B#{>~_Vk>"B/djo sgf`)1+]l, LLlp"@#gK{;Q#g ` G?cH 2 (;99}vv@{GKs ?5XT-62wsٙnFKhadc75j*b* e5Ej*.EDEUM *$*$TT|>U?|ISRRcfsf+OF 7T3G{ۿ,x]ljapw|\6bg1Ng Y 'I_NۏQ~$}؝Mcj+G #reeFv@;#;@g#g'_7Д_G?=6K]tm|{Ō\<1m{;'K'gU,m;Y3KlrBRb*³_ 8lٙ~v3>Qˏ99;z0_a[ٻy?fvffofg(%?&m@gt7`/131 `fd4~\༜\gG?9&R.pU3p]#*>5?$AN^.66F@34 aK%ohkd_>K'qKwſF/чmIϖc3sC&v@''_. 0 )k_'fgbojig`a9:y1}h!lS_b02;\}fp (/ `7b0*X*FLFGѿ+uȟ_+3G9[ƿ&.lh?G#!3?G`q_?8~4wt;nHߐw@+9Lk agG{k3!rFΎ:Lozр^l,Y>$q13pGɿ/s>@w ڲ WԖr_ Hrn*,~Mx.|Ѽ=@Q@&E$or]&WLI3JFrHbB jr=$ԧҹZlD3_,嘆)$nŋ̭6h(xݠ0bh B&{я-0еhhMz׍DUs!1FqwY8VXs8Bx!r0\DFiYǒI7#Uڛ\wϥƇC DZԄAR0TqhtH;"yKd_ӅT(a\R%壌=R7D+9nBA=i3E8 KwPjJK0čCm4ŸDRytZ ОVdZQ< U+Ii3{:Qf <2V֤ `랇mvLku3fYV8/3b6ssV5O]yܗ+<1bSou.S(Zţιl#' Jb^iDv8m.ۿkHnF'~x2{͚ף[U6u7k'0ZV(n%NTD}Txy՜ц)S *;I`[H0Dm@Mޏ ڨt*EG ܵ^&`wX9RQ:EˉS?ȃ͋.5fL[Iޥ=*AIKlK&n[P[p;}>kΠN!&zpB#lAU0K&+rrZNϰw6*:ˋq1kH'}I.|EuF^<L+kI?I%p=ӚaNl6ش"j޻8j qAo-vL1`xX݋>sף&ʬ=67U8RvsnGrcdm8Z^/YZ29zC Y@{IG~.#$u*뜹~ BƳy qYq@Gz$y#O%LrhL~6m;uϳ!gBL֥!>!u.1uժF9$/&M<4 `DDXQd & lL_fe?,G7we>X?$B^|(K`' aJC0N]aN}7*xީh/RpMpâDK*ʣQI=ƾj?0uV1[Bs T6~*:CK![rL9IXMy^rrh,OdY±h[k+Gi;iRRP~sbQhumy1~/ LΛJd.FeƯ8u:{pC E.DEv-mԆd[ tԫ;u\V=}`M>MxL,OLt+c60u]OmB+,&ocᴾtbKLA,d%fQ|+,htҨX-ʅغy IJL`U؈$8e'we-|@|QAst @& >m!F~5cIzJ?B?5#5e#OxIE2RP-FbqRoe7i^$V. u-p d{ m#-|I\6]B6'[ȧk#)UԄS5Cξd!/65afӻM6#{atC#OҢYt5P?lk׎gY|X~ iy(u%r:٘e@l!pϹR~R&Bf$c<ހ"ӋX/hۻ9%;r 38jϯV $c14I]BuWB9&h V3NTE.CЕ|}X"o+Y{&uCX ᥔ`4&+/a_i:s fwo<N4{oqr$_ G|<3_Yx(EhCLa"v`&ytF-" Hr W(ݚ JInOG赇3@7f,g(Ϳت.,iA {7sTPڍ؎ڒ\CxˠK%- LqhjmƇ/r|JUe$APO nJ._E:d{*ya)S WdcvGTW΍~uc^IMDs"%Ve?A ێcBzgn-'Z4ݝd5<[9]e򀤉ie/'QQQx,vs3/S"2~Էݰ<5t,\TH 3fJSx4ի9a Hj .1h]t[㟋@wY+ 8c؝6Ѵt0 }Q]QC!shdӋq>,/ȏBnIdG˨viFè?xf⪮LʃyWHmFKJ[RL۪7;]C[5_7!P}!F58XN%Guд)*oDIo_利fJ~+ YZ;Q_axemM]i!lH7-Ix"诎Mş*nL.c4a;_Bs: U~es׫ƣ 48M,i2Y.Lwpb?&q[9$E7-@矋|skqҊ B6T\4POQ@a}AE,Y46]V 'U=<[i|)DZA^]NUwaSm(K!I_1Pvw>U\r.?ww CK 3>T%%\vJaC#S-'[$McufRSۉ:Kp¤ Y/~`W}]/d@@ׄ Ì5U@ u' CF47A _zh^#jk 0ՊzS 6!3[LK V0 ]2d g4(ek+Nm.қMɾzpL%߀~oKPI?I Wp|o+^=qFeEx#P&(5-=jiqzLn.6iM- IތuFj[Kd| Q1; jrrVw}1)y'=rqp ٧VxD/2($@D ;@ҞJ1?YeG_-sjowW7]F`0Ѧ(Y RJKBTx^_ rټ&H:%A= ԟQR2YB .kju+HxMZ+mF%lLgڮP_\ xkw7ɧgK|,- G(/+N7aBU^~5STZ5%rb՝GSev zֳLF߸$tamشMTnW싩Ii !i#t׆q?x }nS֗5_-O313MVoF?~K4O{krc0"mo2Y?/(s~- iXKW3iֺZ<6~Åc Wϻ!3VT5*؍1oH;cpo֓KJ2y&֬Q[/WiΦ'8?&5xX}A΍'LP*!x PB< :LH@JDOptCֶ r!]SBw\:Gk‹s36{Fl( .F*&v8µ 71c(mByj#d%3)[כ|oG^+ElM c[|yTo(MzC~)…6Q̋Pc$,AڼL+hk'GN-#5{leQH=Uebz ̱+#i5Pڤϴm.4炓 ]&DUG[@'~Ml艋l_oMGYBWwb"xKh,\MVعC+Ax'ZkE߾@ߗsl+Mp~V!*Z@BI Q-MDZHbLXeRi Li~n^، &!̷هqik}e!J3lR{Yٙ=Rnuh,΢Bd>gi-Rm}ua緉bX\.6yu^ zեYՏea0^!#Lo9v1o"dfSMu\ЮhP)0|F H~qcfVruqxJo7+$OK[Z8E !5U dA?zѵ US^{"~)FQZ} YfAES\?E;}e l8~jǴ\?K`[|YZ`jaokOvҫ@ms־y);;lrPqhѕD@s=ߔ=8VHG)T{tuS8Plb2Yex<8.Чocajqi%8BT쇘u7' 0w7ڜPL3) $OidnS^U Z޻#Y9.? .QislTɼ"=#r*GvUZ +T1%1iUN'[,vp~Ԙ&pC{ַ~R#5N)$2w}uƕw("=r}=f/5feiI7%F~r8w=?Wl0(R u!svXSe]?h&NXSyzx<>-eAytwf ad {X|;;H;w'(ġ#i f uF_(UM(=Z 7U$6y^Neuҷw34NV>4${{#8|o<+#,.#G`yLx䷓ԒP+Hh9Z]ypɷG|fOq*5e4}b[t?)/_F&3˲F(̻w;!.Ͻ#i+ڤrOc5lscD%kN +9Z4ϥ`#kJYpP)å6N+#@rˎ$[~ Fע™w|Q;u3u2HØlcY[ؔ5hR5R#=ntcecVdɹ`!tڠILG)0[%)+~DҖ~YvѳHMVkrlX!Cc3IZ\k;^:^֡{vڣdv SO$јH>]Q7*~;Ra1rQ["#CSm`5b)'6dQ? ۄdOա5(ҟ_d"Fl+(_=aS \X~ÕLŝ/kYѲXKXD($OH?S"붸WcR @$ 20|ySkEiD2_ Qnm583c+U( >j׸d|-% #!PO7b1 B~6aeAiܰc+ӻ5q],.: aa? ɮvbZo3c!GKVA$06yRp%!W=3 M*7 Y(x: sX6@PJ~qA=_cy xw =t5J l"gw%|b^{rOΏ Z&uQ eYrcK.D藚`:$"͋Ѣ}]}:vswPxð+'~=ۜIɚuo;$ ef1Pg(XG>t= ->`PWkf$@U}sf+)06|[Z1hhDcAcaѹª=|R+p$p֣_g$L+;WLMA`\g*FͬZTk4|i#rpɮ=^9J36zL}m;:#,6 (~CɒXk"v6҄ IIFˮdvOϥ6E\+@ 3o L=K٪Yw ecF [2|AWJ.'ǹb7#k-nzT 7ѵDS)"\K,F-!DidP PuYRvhI2T{Ƽۖ!]qe~ ip . +ĐZy* 8n2ٲnm F8U+(p*VSwL^a”?Uh64~ ,Ho`}e gcPwxfy?/"4肇f qZ$pBSrʑ\5!=i$WW-/cd5zR}CU]lfO|zCOrmҪ [W;`aQ<100ۺ% $ _s.ȔM n=Iusz\[{fn2j6~|w[]<)A0BR! jYT6[eCz9o}5./ֳOrQht\JT.%c~t?1/5x7vj6+8-du}0N|0~Ȼ$\Լ#ɗJ%Ľ# ^^#V"@l55x(:^`Bjq΄r"dv,HSIsa`^vK_U!0n$#8} v Pl$uyWZ Y'p*%yM0ur# \{nipHЄ;KVf^5rNVM7aȕdT vK}{YJ發zZ^}z ?/q)b cyeMNOPgq7eR CmC.w 3_ &`9| <5QeufF`,<āIg^ɽ#?\+/jI} 4|C׈95nѾ2{WXxAک%dmpFq i e_E-Ձ'~ENoZ1Oҷ!Z(pLitIg~^J_4oL #[bϚE.&;T={~g31*@{[[YlL:c \mTD veS6c7'2wAo;@ zB= '[5wD9 /\!D/>L[;;zNj Yq0Xe-86`VXߏcPGՔ+( o)׳Bx:Ne䲧g! ܕab9H (ѡn3JSmʃfx:uwAղ" +GlSQc3J]"j2/Pn2 &}i!j4QCw.: W[1R̥0GdR<0M[#?+laʫ&[ٿsZ[]m?`zJD;jԆ(*ᶰ,0V  x;-z!%=wf}߀ۯx0fwkO,2 Pa鉑 ^hЅZ w=fG&g@"ز 5iٌ*%q-u ylK 5QTmUe\qǞmLU.#w5>ŋۤÚa5k;YF v7&٪U6DQqn ]sTWAF- _Q;COM F/G ȸsd?g3#n7Ă Gя|X#Plm!gmNU(۲UMwW}p'6Jr_ifbW35M~ڗχ0f0AL kzD-ٟ~ga)NO SGVOF mH'uS5>q/Ba(.dbh?[1^Gzj2@Es3A*CO+ EI( G."qs7Y!8XYJߏ @NʿZkIi 6Jf3a!"}]|~a/M+[w8ʈt@xEl|=Mp H@(mhוZLo-ޯ|F.u]n ې6UT\Euu?僖K N[}P%r-j&MıjD@=!At>Z&L߯d5և#/ 92ԈZ\ GR}&8C&=哘>._哣f^C P4^M{L'V*V:;}&>_!@֝A@[Q7kG= (͐`c"Rm~Å#A''a,BbnDp 2P^յL?k%A!\vDȐ !75_2Hp>kz&k+&{W.mi#1BkA 4_/ESIG?NSNF N l;}aDU=I ֒FuI5؜ l.E]o"YXnB~&"R+_s8;eﯴߤʧAߓNn;>LBJbk2}kJOt[}|zhハxxV1mf>PyKlټWm17tW/sSltPnV*fBo{n)GIat٬1$4Lc[@* R^cBbL$|XCB?pyIUdth^߿7b&ݻ5)TY s5ÑGzo6O;╢ϩ$qƤg]Ɇ:0K 80?WXHI,2O .XZ6vy2aG N;Gu^+ѭ#}.[u5=lN0,hhR<}ٝAwwܧ WNuk|aK1\|+jB.՟D\Mp;)v8)vIbaJ!zĘ.Ȉxy]G, \ \jmCkzM}Fˬ9D"z7 s:\XDȏF/ITd`ZVi Uɍ/w rApHsH˅lޞHi,8k+C_}CTӼM:XE~x}5b_/:dl'c&WLU L/ǀ7l8gOܿ()8sQ2bw fl<8;V7Q|J=/¥H*Z+(忂ѷ\5581vW$_LNZBK]T 86f˟L} h$& lY?M9]&D&P \8`c^Y}IHDhgX ]o5lIstiwR8`&D,^pyޚOkR glBC›P;qޤZ,쐏Q66SﻃA2KF` Yc\ ot2j~1mov}^`) zˊ1l4=rEgSL M`v<F(lmSFߗ; rTIh;&.Jt9TY}D͓Ʃ s3ʹ%-^3kr^Ty= ,}:ϓFo8%~Ê*#bC("'PvWP+=CH@R܎Pv. 7h{r>$/6J(VaFUʼaW2xc4:ީ!Xnj 1? ExYwh#o0ID-*>[{$D\)>-GĬ6_W$]P-GHxJܙ=,ь[p=p֐Q[$F*SVSzߛ-ʑv,'A&j2FlP}fc>/ax)M5<3]'yܝF{᭼rC *;)GL{611FhY;+~s0"@z$ .6R}Kz)߆/fy_l{& .|O-9{3Ӯdj<ȃ_9%Е)!]Niٻ4.#2_fakw=wbSjnVC P'O6Xt +BkIĪu?j_;QHPfG&U:hDH_|a;yi͎|/ٖ:GJ,Bl}%zR6W=T,a>gEc|Dr W+m!ν!::9'֦*3K TstY/M#r/LX5ۤք/7Mk!?4hکs]ZZ>,!GgQ| Q 𠲛_F>״܆>/Tg@I'$UMw;'|(M[ EO>goW.{!Iի03| vx EbW+ރ6=܊J:fܜRމźE i2D<<6VX/|Iq^2PѰYwUSZ]b ![g1vtf\e ڔCJr6}ib;u1`.>}*诊UPG FR#&ak;V/΂'^d&Euny:c[3wC.&^#нq]`ZrCgÖpl1e;(CK3|ihbnb+ w5'$Z74Inqg;Y[N:,pU/~&b샒L9_:umzSkFkmDSZ &2PjrZ+Q5ǟ-wlU/*7MQ@rvyD{\r\,j"VX0~sRقˆo5eGO m9fk_=RxJonR#aT÷8)"14vcKK*HYLsV|#]C-%0 cVOfC6BH$3h^dx2^Կ>.6Xݏ, UgˀH?A'fQܕZMjUb}4 yi0w<(k _Dm/ZܻDą'ūh_lj,]F]t's|RX(ʨIasħ^PjjmTSw8O兪E]\iIʧp@y$FtUZ=4;?7h ! k♳;:݄DCLg҉X՞S4mw"=^0n#5zb4G5qWWJ,HUpNz$@B,<á*au)![xGb4/jXkVHK{[F̷tY褣؝;d ]4}VCʘ yKmfl4>T4ObYc't懳'fKNH`i{%\ropE@")^&gJev3F^17Jm`) ؙd; ʼ&3VYi,&sr? G!;-Y>%H*8ns$ڙy{a( ر\4yrF|%{][FĐt1/ #%3́wiz`2gP3{w8f+ܷP{lDZKmQ:/;`$5@68z֯G9"HdW3ް b~^!ZJc2v#xŨ?,1r6;J#BwC%u!x尾U6mHf" #c`OIX`o(ÙV]_U9'wXOn-nam_:`e9>AF6;2ns9F< .?n=)CM0(sE`K^&g% zeO@!!dl2KkX"SͭiR⏾܌D,i5ZJwѧ1r(wK:ص~-7N`-q5kbPkni8 Gu0~C 77;cuzacf<%Xk8f*,f*S8xrI78s p(&8x-g k֊MiS\D&к'8% ر\4yrF|%{]^"ya~ZIĦe$mvf1Р1nm0}kƒG\ CR4Whizm˘A; KycHxpaՃ*,{TGJ4ud]3kB 4&GȪ4?1P!B'enu EIp`x奾TU"6 oUb ,9%A"Q7U"|Z*$\:{|MynM|y9 A;a~Ub\c6ErE}խii4"ǺXp3ߨx,2S`[1GmjT . OxzN%!i߸jf,]:D >?z\]W,3ghlb[R}è\\fdƋ| {= ZL `d%b7@IڻsvxGLz`tP+L$Bg|.7t>܋5NAEeY70B )smJxN7vb]bKZ7@0dEڏ?i[@O5/dּk: n%15cRDE \ۻ2\cg<%^9"@ f_XV;~NΓt5qԐ%?@Jr T3oCE:thP }ۙ\ExQm媴avNU^؀$HƖ]qEPA evX=qzA>i7$qL=*h6aew._T4+X0n nc0~nL*_瑟Cf/¨;'PHެ'Y*=d `XMK ,ҹ+б"ZDF=Ƀq %Ɍ֋W &_G 34v)PW+a٦cl xĎ{T@"ƜƧE dh,BKldc/z a.VNTL?$Ux3K /YTZkIF}ܙ6ww[  }3S Dy_xDY&!V!'d/1 ÉS)U.?&y@8MNp1p.7Ik1<6 —v'CGoJF'/졤W.ɜ%_-ygO%#;h4`jooW\Vz.*XMtYkW8BKե`{W0pb ?Hu}wtY_*!eۤ"QqX^:THgTÚORӸ"ŕУ]b/:JJʉ\zgU[@ 9bwď󠫂/<]6^ķO[:;㊣qc]VF8_)…"!?7!5*G9J65ZrWLX9Rv4qlkD \b| pK>@`RSLg5gjzetmꃔn3Du#wTrm!A%_ЅuSO3a]R6_n[낕C@R'bȊٖ& sW#s#$Ow@r9kQbsQ)v<ׅ¢+e͞5]DV$aV)OJ3ۄ)݃k;DԂz4{YȂv XA rB̛{>3#/Nv ;Xn5xJcl jRHSso+KNW ISԙǮ_[J:A@}p܋v'B ؔR*WүSxoڜb =,.U MwM0Aoki`lϚL2 ?%Y#rf 9 3OdLdYݵ(Cmteh\ endstream endobj 99 0 obj << /Length1 1852 /Length2 22023 /Length3 0 /Length 23182 /Filter /FlateDecode >> 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!:> endobj 2 0 obj << /Type /ObjStm /N 92 /First 745 /Length 3601 /Filter /FlateDecode >> stream x[[6~ׯu$7<*{q{b'q#q4HBR__/fl9Upi4$H"L3a,3L&LK2  fdq̄c.#0%91X",s,1`0 "LI˄)kڠ3Q2RX`i k*ET-f QLed#1$bɼ;'bdH:ˤ&񟠍~ AS$ƱDcAH52P։ -T1 2@$zqw:&cZOA%aZ'YU" Xëu *B;X‘ Ri AYA>h06Mf` g`D0%V##!1aȡ#գaxYh)e%Y] # @y05 /!4KR*䄑%o24K(ZBejHUHC>GJo`1{?scx>m2_lOu%D7⯳ؖϱDW|5.y [.Ӷj^vL5/:RWmY6;5*ȔN~cxr_^/M˵}-^>3f.YoȾr_Q qϙKÙˏ{d.ܐ?,sy5 ÕJ"Ji.C H%֝M-L4QߧnuyS Wvp40C@U7P ?ZNw%I4=u:PڷuSm-|Mpz:f~=I Qt6D Gw1#fJ'm ĎXݝY'IX{:ZΙj(cg[Ml蘜WAVZA6(ބ絴a\{wT~, 꾤dP~ R;7&cN05%q_/GiCƝ!d L$=2=R~Il3 o4MT m5*0ih:AGX}? =<ΪYo ͓w~oϞ.VZɣb92]TLG9Dtnheܯ-',_\5MZ&"Ai.bh-wf<Ғn-g_|xΗ|׼/yk#4"X&Q@!/o^HDbL``$؉'=0>QDt}G18OSkb\f5pbrJwy.h,e-=W6WPY3@'h'Jo2/? }|h}*ȫeZ]&Ueee10#}=w=n$)ca$MccsV:?2& U隌/;w, 8WOpdo)"gWd$I qR̡v}>NρQڸj?CGj'Ob]Fy NPI᛭Rʯ dz{̇A@ Q*c.}VYw6 ozA^pvuOq',OYEyB&_,{{(9ev$cw<:{}g`NK;=喞}[Oagޮ$mMD7kK`l֭ҝM?li%x{@p]x rvw~s>" qOR`ݦȩ,3oN 7]΁ӟCWdHW!9i^V5ȸҦ!$xUŒptԝ%9}sў`5@0 {`n_0{<^ݓLއdw}5![{`Nߗ+s:?@_2j$%ķt)o;~ڽrؼx׼ԼӼϏ03cS4xU!l}it;!Gk ܯj݇/LvS;6iFk[>s+Ȳ1Ͷ&*_t'O.fWt5.'UM[1Ȳ`-Dt=IbRv)]zByd=/ʺrzX{GO ˎ endstream endobj 114 0 obj << /Type /XRef /Index [0 115] /Size 115 /W [1 3 1] /Root 112 0 R /Info 113 0 R /ID [<07624197D3FB277B48A6598E98E9520C> <07624197D3FB277B48A6598E98E9520C>] /Length 285 /Filter /FlateDecode >> stream x%NQk( v,X+`G-1qVw퍀J0cjUFh A=4l*6NP>X tv>dm7a0D`FUm2'0 Sp Qf``6!IX%ò͇PyMZZQM,>YZS[ZWm@! pp#%'22' endstream endobj startxref 184178 %%EOF shazam/inst/doc/Mutation-Vignette.R0000644000176200001440000001131114071641060016761 0ustar liggesusers## ---- eval=TRUE, warning=FALSE, message=FALSE--------------------------------- # Import required packages library(alakazam) library(shazam) library(dplyr) library(ggplot2) # Load and subset example data data(ExampleDb, package="alakazam") db <- subset(ExampleDb, c_call %in% c("IGHA", "IGHG") & sample_id == "+7d") ## ---- 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) ## ---- 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)) + theme_bw() + ggtitle("Total mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() 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)) + theme_bw() + ggtitle("CDR silent mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() g3 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_r, fill=c_call)) + theme_bw() + ggtitle("CDR replacement mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() 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)) + theme_bw() + ggtitle("Charge replacement mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() plot(g4) shazam/inst/doc/Targeting-Vignette.R0000644000176200001440000000607614071641075017127 0ustar liggesusers## ---- eval=TRUE, warning=FALSE, message=FALSE--------------------------------- # Load example data library(shazam) 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.Rmd0000644000176200001440000002262614071631673017326 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, may be 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(shazam) library(dplyr) library(ggplot2) # 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 a 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)) + theme_bw() + ggtitle("Total mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() 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 subdivisons 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)) + theme_bw() + ggtitle("CDR silent mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() g3 <- ggplot(db_obs_v, aes(x=c_call, y=mu_freq_cdr_r, fill=c_call)) + theme_bw() + ggtitle("CDR replacement mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() alakazam::gridPlot(g2, g3, ncol=2) ``` ## Use amino acid physicochemical properties to define mutations By default, replacement and silent are determined by exact amino acid identity. But 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 mutation 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)) + theme_bw() + ggtitle("Charge replacement mutations") + xlab("Isotype") + ylab("Mutation frequency") + scale_fill_manual(name="Isotype", values=IG_COLORS) + geom_boxplot() plot(g4) ``` shazam/inst/doc/Baseline-Vignette.pdf0000644000176200001440000076551714071640761017312 0ustar liggesusers%PDF-1.5 % 11 0 obj << /Length 1778 /Filter /FlateDecode >> stream xXK6W-@̒" nAȡIe&_!%"Q#qq曡~zyXL r0 IxQNC2W,FN [rٜVxoT[VM3c~WW?._U1FL9Kc71IV_Btg%/dc Ue7(|;K/L\L QӋ.҉~Uuf+b$WЀi< :!8|FЉF$#4f83Uxkp&=FIJSfA0qޓ/ VŹI̐һDZ~W!̽ws&gS)9 Eզf \ oGܱ#/\.Ǭ?% 5O_U5uku2g}T f)a(! LH, %T)|L7mJA6,a$m^{YfYj7g2JyEGz Q0U\Y^wӥZXvͣ$dONPYCh8DHI%e\-hxZ8p8#aypvclM!s !#{ ?9;?Gu-˵* cy3l~T˼l^iRv`|r+ID 6]Gs;*tF.J?ʵr|ieB-MpRwj>jf7bu2x`˪)& 0 g)tQ]vӗ%׍#ySֻ M0F6[.a79yׯN.0,Py@ wQnUaj8xB]n د$~qX?`25RIj[V<w*J|vr2Cn^S0>u1hQ:S8 <<&O*:mbTWd*M1""1}C >쨥9ΚpwZ9x co[{l> H%jP8['AI)^V2@/_"݉|߭=׋Z7;e_cCo[hNq]}xTJY91SS~݁9;jO$vSO~:/+Ά_V 0uyOYo]{,e~`qT},#zb`描U=|6H{PCo ̫ M#Q-nwJhRrk$?F=^1Q pQ:o_޲$ endstream endobj 39 0 obj << /Length 1949 /Filter /FlateDecode >> stream xˎ60ҋ dT44.R@!K\[.%%~}g8%j7M䈜š-W~ry Mb}E& `/~q^6u>zcʦNKN_E-}j3.[wyţ1f\Yw*YDuTy{KFc@;&u=X:Yd]έBAs00舒TPj ֹ%+ d'ZLVc8dsE t 7q x9@;d(Bpm{)ZԬ>'hJFi^-_Zq 5ϖ9뽠}V̏YS/ڧx>#}4($vtS]#},ڬC1ͨneLbEIR`VZ+X9o:Id0E}/m뛾r&fhA߿{`l%mB&ۧmWd 7 Pwdg \gN &x1N oꬑ,NX@.UE 9JU4R]DG%ˁTg aLV:n65 q*cP&%1$t}m'g^w6뷭h|LC)So 9ssG}2m [#ն$kɤyp=?;8nQI Pi5_QFP''Q^mDv.iK*l UGRe dd01җ*p1m_36&}LNl7O=>;n=[yY6YZ^nRs1Qv2=}Q?oG X6MYm:Q*ozx=Ԍ#vG؍XlSVI!vPMgΧYUڄ4tn#=`q5~u3gWVqvFOzKZƟ2-,'ziez^hqyiݦ3<0S8dmB'{!L)+5N1[1Z#f/36 ⺡nSxIߊ\3Pz$fX.n:\.*cKS Do1oRj ǎ,m0d. 2PQ06- )gI*WGx#LM5i%*DX 02 1g$nѻڅ93z\IcnL1vZjdf&s@և_ӏ ǟPDlߦ 'z#fc1o;}[,p9M++]R40z~Nib/8/]o' FObx\ynhWːҵ:2-B*anz΋܂;2 I4(K{Eq#ucٸmSaTڏ)zZHEСWNϙ(a+Oygy~7}7m endstream endobj 49 0 obj << /Length 2515 /Filter /FlateDecode >> stream xk| Ň|q7}MQŁ'PBR>3;|,!E~pwv3;xqW/m\.6T-8zcVRg:j]u[ue#Pɿ CՕ!Dm?LH+ !^H<12aL‰//2UU_4eUkezC_ BF{~>ѪM+\tz.alMBg_K·-.<,9Yu 50uᮮya#;khDc$# ^VO4i 0AQ]Oͨ\s uu2kC;o@C%RhMHuͶ*wGIQƻM>dĝ6BY"l[P6ޡ48R:hpC_iZ <% }h{1_Rڞ75R|\=jaESl]Yz7Xjd()4-q-b@ΣKNWݦEKasnJVdLd()#sCdIb&}LA^,hFRS!-UTmM &{h)hN[x̠oZ.}jkim<+vn=Yj)Y"2(ZhKnf yNmys+?%51![>UŏѸR_єǼpؖ;rm[L(YTʬmPn~]6h^-@n9XWTi%2)nz  3JADKv ;2oh?W[#3ɺ{v2W )q,c **::l) ub>«n '0ev_ӹ̚DHj{WӇFvTTܺ˱ݼ9NYu3`B4oݭkFخMͫm ~90^*#$!+wMţEnxGbNtXsU vYUuX krԇ4m`9L+NdBzϗ_#)סda~Jꪏ/KPÐVr:MnЀe ؙƉ*z8O&Dj?0^:W(WtS/PL-6@"IxݹBXPY|hd^+ /F!#>WD!y{=ruʤR$iEF _(%&(bIS|ڗ@7c~/_?@̶| 8k]{7_iE`3!eȰ`&;kwބ΄U#l"{ o,8IqGu QΣfD;ej\\ci=`P)/cŌmLQhNXLM@<ƪ4_"kȮ=Jŋ(-5_[dSinԫzfGD~.C OWԍv=4a>{e;N|ZA[ \5-}fed^J=|r+]+?qe.jڡ kxH~i=cTu3?&Uͭ|;&WNFbm$41h:=Vٰ &J > stream x]ݿBHQ@X4\9}nsq`8$-E*$]˓s苴;;;;3;;3;xv7gɳDeq&f,4΢XCo=iu^UޗBY5b!Ū/Aoz˿>{)1qhYX8 \'6:-n-~ٗmަiQcQIYۮh?}ӧ9l^^O+)~ہvgV@}Â^,N'PO08+9g v5H`~f"c=TG6ޔ7}*1o/@_/d:oCS 1acaet[dm ATaQŇal1`3 BrY*@ `nu;?\O?/b®~/W?V:3%|7QfҫQfʩ5 ]j=$b"Gd&B$V0~\-7A8VprܻUzR⿡|{GNL2Rfka"&ڕBY/sd z}pnYt(~MF$tL籈2o /Κu6)k){H-u#wM k;TNcؑFJw\F^M)SX3s-Noc QfT:o 8GZ?V`/nWFI2rFK f2`c/I#AN>PEHopx$k (;Qn;pWEKY4_7wls^n;#c %y> stream xَ6}BEk$C23ݙ`,v-Ѷvt8:<*)Kjng,u.\kk+WpY$nY7s\׃Eȷ"7q\+-m,~yX-^w;Ur+c[ͷޖWYry{leY,du,s (Ŷʻ>~I& $Jxۿ}7HXhc [֋a ?;ـT\xvFDKֽ&-;v*Z@ʼn=nd uS{e,nqm.]W}z0<'t#+ƌTTǁ^ \:FCBs{++وk HK|e07"KѬ^0xQ56j-cTV1ʳSYr/TGsӺJ]+a3'dX k "-m ~W뼒4)HMӺAux}K]^Ϋ^=ޝEܻsq?/ߗ2-!nrO ǓDBHt^{yӮLa@oqFP4_{}׭ @mqKf8 .=SFA lvni\/ @q @[[87m)1{C:B"Mιks pKZ  վ)Lg O;N#1ދã`T=6p~i}))L@s11MD޲IQd&Ja|eȫb!쐋@*ti0`|zEc:a LF{NtZq^ mI8Cig(T"c#1d@KPԐ}AܵIen\hZ+!@A,.rZFky͈lj}+/Yȝ47AvpcEh{KFLkCRV88G.WL* G3ޣPM Je:Ύg:t|+8#Sj6Kwxg8ۺݓڟ. r%ҩ$. I S2:M^}\Sl?ɶoΕ\L&q?"MYUF|(>tME19r*2|]Lnpv}yj4Tv+^~2ղfpDB( p9/eSb} ǑU*E|6r { ?|}_JxgϓArE&FɜKKD7ݡ" prd@-_Ny}&㶯vQLq!ǧ/VκM.O]D_LC<_g]w߳Yei14@Q{i@MwOI-ڕ++كaYl.',OBĦNV^<\>*1Ϻ#KWT'ש _8̸J9[5Gp ؃up e=ZRu4 E ~[]S7Յ~Ħ}$Ѓw# w`D̷SxXM.օzc˰4f" ˢ}?[^9Z ]ԫٸ%d(Q<׵oӽ`BR5"\̾ŝuOƂJlJ"6$NvVFNǣ6 YvRKMt ASpo'CO|b VӯGj[EXVbSSZ XBB'7/KB) ~u} <qː{@f2|6»@ Tuhީ3,x*Z ۖڵM]NEŲ\i.L [ub]nsQ0E ^3sb E=h_ ?ږŦ'uo] Zv2kT[P*> stream xYo6BKXW~h&˰ EoPmk%Ox_;e['-`G^poq_bPO@R>:yqD,^$'kGhZVlU׮ړ6mvϫ_7*,+{cXE+>[&sVkPx Idg]iI(J3`J}o_&k{͞;Հ gAH<e_ͫ=e:?%K |T,|z5JB4mQFck3+?/>qLmQ#ڢ*2ihpvk1ͳV6` Z<^$X#/!򮖨8KnIpI &=EY쭟vPơ. l>[&U$JSJR(?]pql[쭑^w]aHzEZm*I rCk3+FGCiO˜i)MٴN뢝bQYc30"tK~{)q')%|vQvVi0Ѽ"jژ;htHF99907ܳ>>J &'M9x3Ա B+w3VA$6rjK5oG vVV-Wim#v)0 V3G)=nj|= B߲@ޢ#6l@38dpZF>͋`xC~txt.W ̘[Z jDAбgg,CPm%F3\$`iK,a,}rjF\$Ǜ~ > s\@gv]$d_I${~ w[5`hjAAp-+փ[.X@{:Ќ ʔȥ=sM_VjSZN*18,4ݲ<0 J[ ߸z%5d4,q> ѡ)ZViQmDVZ>ib6< ɐ%A@f :H rDǹ67v%хC kzٙELPTq5pNԮ : \ǃ`rC&vYL/vݩ=W= 2 0:DI!nSrLtEI9 T _ Ӷ4 ͑xwAXUz .FaFW]$`2fJi77LTI4bgu5z?M364#XԺtCj eOe }z8zXYɱpgs7 >ش040PE]C/BlA2iEn&.jԌT"ؐi5 6r^ =؍o,>ciZ%0s1TgΪmI밈_wG@R7K|梶-Ur{rls=-ʸuOd՟P}KWV&ƔPZ~ US*@}g_:<}8't-/U\ >-j3˷.C%_Wf[P\bgLp73YJaC[,xUO莻o#,n)E mּ:B6 endstream endobj 83 0 obj << /Length 443 /Filter /FlateDecode >> stream xSn@{IYwЋZM{j)ImcVC,<wFjzlHafg޼aA+D"ݛIE>CO3()/ R]B<M`CT x6L(ChZ@R޿9\jD.6eZ4^l aW2+L5TB,e Y]x~FO?s>n7  ȦTW(R"7\ jD:gw:"z Ls*,V2O> /ExtGState << /GS1 89 0 R /GS2 90 0 R /GS257 91 0 R >>/ColorSpace << /sRGB 92 0 R >>>> /Length 965 /Filter /FlateDecode >> stream xWMo1p=c{{ME+U$)A=3&u<=ϞRz)ƻ?_';{xPӨ}\grƼ,x)3ݰGi08N8j#eŽj>#TSV4 YW![JP:2YEOșҗ$Ҭ-gK~ ׉iu,%45j4%gmL'%~jpyV|53;ҥW l :`5_Iq=W l4[I#_Q[u\^Dzܨ)v\@YtUCh[m# zV :AJSyVj}ZwݷstSr@Jڃi g׻7 yJlq5!X=:&x7[ԕ+* 4t^HEMHjg3L)`6:i{ftq?/. \-\&'Y6vGv=ma?N:9-iiT&J4Ta9%A^B{zE1)plJ}sAJ~kj r=,vc endstream endobj 94 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 79 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 1414 /Filter /FlateDecode >> stream xXIo\E_GJ" A"9?ϼV~ZXVYu]~:{+XmQa٭wo~լ[~MuXk@@[>1Џ+> 89(u(*cVV]&P0.8m=>9qF1w8hj72at"mM-; PG]Ud敤ST '`Pde霆6 D-|KtgDg )>X*fB.l*dHr*낔ǽoչv>@׮+fiY:Ů6feOYo{;>ѐ5Gq'"ـ(ҲuJ]7mRIލ/~ߟ~޶VL-szV*[)R^;(͙ Aub,-+Y[uKtL4+S=~}꯵a]!BmDŽF/ݼ.2Gl.%?JoDF}CBbCVIBZ\ g6P>F}`Τ`- $ p|3Vph\ I*MBlJ1I}0wgZ!97y6 swKS3BϺs\L/T'u󞧂W:,{7×nv:=w4x i|VGtDcKHG*߶pJu_ٿ|cL"0ڭneX*ȧb/E9lUFw( ̭\ GFwPvCDQ'n ejpmح[oSb0҄Gk +F)hon+ 7+bý | {%~4SHS69]~-ThH4d{j k nNfSK8L+*C )k 9S;dub,-+ 3t!%vL4~7enH M5@Go `=`0B1m@ YC0<.g<>d֊P  w㋧;} +9 뒟xt4=V6#_7A0!zq=lfxCZ[zu5] bnލ/>ηtx6:Y#[OzF=SM[dEew6t}^5έX񘇈߿;7bplg;;l 3,G[u][Hlê 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 740 /Filter /FlateDecode >> stream xڥVn0+BEr%u!M^"`%Ւq h7C$$I$,yF4[60xǚvP`b5;[ P3ڶ@^wBjUZFէ%Me.4p=Nq<,CrzWUla aS̿X,|Z|X=K)m'o;噮nB9Ea|kw[a3 6<cASdzՙ#)g`h!C:\4!,c]2;be'~j䚧]1 KGqI:8*X@SA⎝z0"'m<ܘJK$eU@@`;hY jW7* :]YKVmwYZ5y J#E1M뛬vJb}u.{}~ fOM§dGE~M]7}75ޔ D;n_[|U ,ˏaJt8| endstream endobj 80 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 2332 /Filter /FlateDecode >> stream xZod _c"~K6@ 46C7tN!~I33ƴ?/IQ$$x柺><~>x376}aÏ?g~>C?9_5N0`Pmv&bs5K9a fB>bqG8Uy2?ȟ\ g/0BZB_Z `Lކ%OZA sE)ٖDz쭋mnڛMn֬H÷-67IԚ;ž:̮zıW˜ {h R /E0˰h2|E}Sk@e&W}[bw7{cB*A{h"/ ePj\Yf !P 9M4x,maߔ=`p ˝a<}a>vU;{d6y @pmU+vj6p9. %ZˤpO!u~U #p|2na N8wg@W:G: .Aug0FUn|#b8t,ҁYDeoeIvyH[ƉR.̃T.K˲;дdAHxԥ5i4uXIRr͇c>|( DH+D}ynpFp(/pi8T#z@Hp8uԔL}vxrx}/9],C7ڟBw5W DI7981|k GFphCI6C/K˼լM^mm<&{9k6 .Cf}C_:}=9 fqA endstream endobj 118 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 104 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 6855 /Filter /FlateDecode >> stream x]&qWe@o[9\a ȿOUSdqI s#vIyȞOWO_z\~bIcӿ=~|ٷonSg޾㷿gOo?O_)=}6Qp=\nkCzyzֆ%=ݭ>|M|M) c3cv=ZQ{Ůr-HAvdz\Q,ALj?zr>Zr܏e~pyg^hKe@d@kWף''jiVAH#At ;]=JߟjO^iѺMKh BuFe%f/2F4qCKz0~>ceh&h~}2l4^1_MH,v?1jx%x[t-^Y'tzLǐoѣ ,r]16KxI ߗjS"0zM㕤x0W2$n25^2FzJp?d uC喑$7.V}Ъzh7=o I7Y/$% U㕒Mq?d5z|{*v5^z_Ɠ/h'>}2ZЬ}2jeT4^֭o_qc~x꘏&4פ?=_%|Lϸrac }ϟI{~MH/ t sQF$=xH:s Zzz"^|4%H:{NjkzzL։':0n/ckOE]t yvk2ߧNjDt?e<,::FWٺgd<Rxx1>2x댗#^'b</wxC@k>%OOwKL!鈗!鈗1/hc|/wGd<O1^ESQ#^2t?UAU{:*2f*2zG-}*r$OE?@'?F_:OFFFDS4"s<҈hD{~4"yCFDORH'iD=lH#lCFGyI#nf#H#sCAb0P\42l -T0hD+҈i42MF`6 #v;;5G'&644"zI#'h4E;>iDiDtFTf@#A}҈߮477N FƢЈ^h44C id8 FFèqQwhFDH#r>xFDH#yFlR 4f$i䦛'F{I#m9 Fl A#6@F#Ј:1Qa{; ?Ao4Q 0yFl"FlFT!e@#s砑ˢup)Јu06 u̓FԭN@#4":&@#r?:(-[I#m hq8 Јn4h4":A#JI#98/iE#4ҐX447g4r7$҈|F#M#66z64ɢ妑F@;F@F7nrѦF@KFl=mx4BhĞ|FP/1 4Bh@#ԋFP/hzr[E#ԋF_iF^i{ЈǮzkKcnZ'$ή@#8N@NB: u;$QP#HNBݏ0: P#RNBݎX:k,:北IfĵIfdr$K3^$I&[LgH&[Ld5ğN&w2itN&b$FdҘv2Mi/sfN&rxG2Qi@2Qg#SLs "ȤId"X'14gF21idd2'uF $%IR3eDI$uFH7@.F&jHD52߇A& *vaI }dAdxLٸ~Θd2q2xH&u?'8(T@&H&J $g$dj|Ֆ%|^'\u;}I:4@#JФ4H#mbOFjN#iF:N#:H#01Ft>]FZ4"äN_'*N4/eLL{F34FnHCb^Fh8,a+Ј-4bϧ@#Xv!Јݏ@#FFlx)hxF&ӈׁF~ PFiuM#ЛFPW4Bhz@F7Pg]i+Hğ-+omma("FL#R!11@`|$FHYǞ y 6\ 4R2122'} ,RόfE"DqʐHeADy<$4iFrӐ:Ca]XHBȳ4m u _}A gj@P`}#!PeI@tPw?2®d҅ȜE?J`*OLA><n1_pMy zy/d!y<܉Lw@ɾ ;ҚQ d N8Ge؜H9*agʒ@Xeeap(fI͖7Cileep(l $f Tm QLH[6̒- f,18D-d18-s )fCYe!lَ_lie9trl[4jٲ, d҈ez<Z2li8P2lَRGp([#8-mrN18d-[ dIɖ)Xe!, &ҐdC ٲGlcp"[#8 %"[lYBGp [#8 - @Gp[Gp[#8@-ǖ[#8-0ǖcv$pcte;r418Č-{ !cI0Ɩ9e!_, bCpBGxUV0{4IgxUjda3P0񙋉aUm~L|t[0Ni`EmEY &^T?rGB &>{L|܂Q &^yFxQc LJ!È3.Ta&\4:+/o67/_7_'u^ & ea+}9M|%ċĖfX&^$*70e҈ &zs7LLغ/_`s_p&:YW__7Wإ L|0 0L|KNLNaT+ċl(19þry6ah=GAQ7C54L3|jO P0ȰJ.⁉ r3"18\aeT$K` )_o0k,uW hkJh׵L|\/ U"M|㇉C r/ &^F&tn/ZxX|(kυ ` aݔNH/  /kГkl&^fj[hEa.x=¶,(ι&~44 0ԎMiM𚃛xUG`xsGxT'Grxm⑪_&e̷lPFpNol;glBk mo&NM k~ k~Ws73C4+Co9߯kʞwCoiz[3wC2/Coe- }l2,C?MiyC6C[z3{+Co>f' ; =_CYzM 7C_[a՘ f`Otgߛ }>3i r3x9lnS|L|{&^g̦9n0I4u1L|\ =@Owfoɑo[27=7ߢo&f/gv0k7K o%fd}+3Lz~Id׾dPkiAKXko讟}.MCًMGԁMֿ~T&^/m&>OW(͆7]SIU=?y_ PQy _6"T?8D7o}o wzi٧})?4U_R^v[3tvK7 endstream endobj 124 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 127 0 obj << /Length 1367 /Filter /FlateDecode >> stream xXMo6WElF)Q@w4-`AKV\IN(;-:IX$537of(sfyartcL2bQLRm^ΆcoH$/wx#zVME/'`3s8"D)s& 4. ܬC?r uk4"im0+s#%nƀ0]^QT@$#FĆˁ0G1݊ #@aS7ƥ^DEQl4*"̽}2[WFNaУ>iSlQܢ$׆Ų#-y-_Z,z-r!vE[M'WVz rOO{Wm'@^,PR{ 1"4`LJhfuO!mbVWH?1xk)`u.$ %/VSr8[zfdyjh7\-*E|/,}3b;"ٗAcJk hۏf j}ǖ$DD&>G$;Z&./T , =0д" qnZ^&¬j$:[7v\GT:,&)VY吘^T R=W10[/.5ݩx.ŝK09$KMR?Q!F{8HHQKgmȺuv΍owc5`{1=?Z(/^ߌ#O.|&>Fz}euu4Efb(ۖC ϊXQ޽ ɽ5@`GL>0ASUm8>`nР]`ƓCǷ1n]y鵜EK?+=}.=y4?g/?91Wh4I]|ѽϺ+UyUpԩH$!*S')wrQ1CDjn;n2M-0fiep*{\㛠}!W١ze]{9?]/t_xu^n:)#.7#T>N` endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 803 /Length 1640 /Filter /FlateDecode >> stream xY]o6}ׯc~E4i[[4-ȃ"+V[lHΕVs Q"//Ϲd$3(f 9fcYY`.R&X)RTLJ(ʤL)DFӀhk(_G@'@qCL.t³p8>f`TY9%FE c¯|f0^r0p&9 5&KbCvW=揟^$PS`0^~4UX6O( %]}LXRg; 4JH>|a&,: ^:j#A( jl:8 *D9$ yXC4. `*fIzЮU )4)dPiG)(p<. c.YD#K z0(ϓ10B({d0RdB(C dS:{&^6 `ɼcjٳgh뒉Un˺Ef]9kӢQ`w5*ܰ3 $}=!_ am҈k쮩g+t}3q2h_c&7Q9F8g{ 7%`'r1<>d~USѱS/nXNN;!#2-Pv ,|l2X)s-n({?D\S&H{7۽>pi)6Pڐ0݁P+|[!΂ <"Q| kiatBUPa+PAir(A"@) _48#d.(M'w{ߝ&nq=_t"S" cv'%޾`R_0 KSoQKC(klǚ[Zd8 `[.ɇ}Aw˪=:ֽvyj+0 {2I%Ia(/agKH_SwqB\tIR`2a3T0= >؃ۅz0#wnf^vu7[W- r0-s:mɞW%8bI`m;1ZޱCU؛y{=oE9V0njjoujDɞ*U&^j8糲]*ǟʶ*LfTWbiBXO>O.j7˓LPZX3q|p@#Xb|;O_'%~zV}k>./K(HR߉IUg,y׼iqyjŨ(Y%Rż-EQMr\ވJ1ɋiSi E C̪xbEU>OUS1Y7΋?\껉.Hᅄ78 'oI7җr&g#~s?fV;YX?oki%wt*XmJlrX_𽹷 Kh67H;RL83~MvX 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ڴeTʶ5 xݵqh\;]w>7z̥Vͪ$J "f $ޅI^dglʠp5v223#PP9]@.@^%@== :;&y@mP90;V@1˟l *eڀܝmfYFyFhLƶ9@ PWPQH(+0Vuup91U5u)zARWUoAPP{']^BMDM[I,7՟ōojN -]\x-\]ANSrlN@[_q7{%_ )'I/(ߓ.!>?5mpWKcr?))}[ٻM]]\Fޟ@3\?mM]2=[oc1c{Wg/dl@-{?{feM^DAFRBUӻAӱgtp+O=OnfN ;]fb ;w'n>''=0733{3W&u{+GWY]#aj_zcfc~`nl 2!x;.N@:7B`YK U]ɿ]#꿎*95z̀L wIPsl=whV9Yhdbj.b{ [eRslߵ~X ,\{= >b>?|LrJ*"tW6I؛̬-c''cOw-rpYޅmK,&F{{ `rB&? Io `dF& <'I?70W1sxW%O^L,ߗd` GĻ? o,AW~wv?;=w./,{W]<2/ӿܪ]Knª.N W?B]&nDŴFiYqƒOIט7Alb*Rc'Vwli Ze96ᨳ㘰0(Q餶ĪGMET\S vƞԨ so(Wa^͙bƜƹśH\u3!LE8!x6JSxb\2Ty ^s Qx-s 'vWPFYېgjfB= ؉ፇo+`ϛ~m{|'3 !^p8f=WE5gyGŵkcK[;"fq N6Z<ꤕF0!D2[i$TTi_*Vly}SSp7:E~ً{֢Ӻ:M""xD~l7Zͪ#~'zGl s{#8%6, gft7mяPW?ҩ>w~L]kflcW\F} M.%O7ۜQX͟iΘz URٕ$M^] v0h`M*Wr_|+cim9ːS dZޱɫ-%y2y3"0;j› ϨwZḦ[7|XguiT@KΕS-tzaE4ͧ!p`/iv. rQl/p1m䏯8Pԓj]o!!Ҟ0;\"8簌M1oҎ0E!|;y6b;2ipy3rJϰB0 ,E.}V b7vJ7\M#hWEvd I4EP4/̀D1]mK"Q1tP>y 4ȩJW&,/L4me Ȥ s?G3hާ|T&O招ƋL*Z{qrX=P='=s=LFm>;kQycʶ3{<̠^K.AA1o)V%&Qrتm@"+2 h:.Hlh{8py`bR|A>9+wu~'/qg4|Bn$\uUh 0Nkцdzѫ [ED,Gj=Y{ݞ2{^ ?&D,jeK8E #Ur);:CnbHџ"~$r3cڎ?jݕ*j"Ug% PJYLm) {0K0_C41b8(pH_dFa_rU$%eIWs< =cgg~ψ"v!LC\X́UI?uN^HG\imbf[OmQ!nh!NKzwkAyBI$GՇ@?{)GrmOP;GѕJ&.+ySu~JqRjN=yh#w5L *zewOи#JVt_+7 ԉ@> J ܅=.YPq׎sЬ?SʷWn^H+Y.MFU م挈pԏ1*CyeRP:=7l#??}.F^<[q :6p)&۸>FudxP壳){ȧW:U PO /N_b5iUې>&+ _=ZPrv< wD 5ElIP Lgt8a q$pd*!iy,976{T$vz6|ӳ#EECE]EȖ{_FL5acb~OR/N1<ة )X'\AX>Ű$O:j \;Kmgh< HgCa!͍$!.uU'|[* ] <1+?D-YsC~U' 6/kGLLj3 \*Uj+0P9JW\ D/<cK'Й5L}))OܐHЊŘZkD. VۼoRhc$$bsh@֤C]!+q]浦*bc(JE QkӞX %xR bؘ"i)}UpOm_eOpDL%Pn$htm^tKE;lRL P ;0Q?NM+'8KHu BBj=r>3#4J<T`d2\ S+,she6[R!bʨ0.Wʢ~H.Ȳۯ_nldq<޲C$D7~VP%<\txD6dיJӄѭ6';?orlP}Vk^$Cr*v.A;MW$h[>0kڌeۖo ZY-.DZگ_WW.mRj?v~qIHP}8XB®5_NnԤ оy_"_߇, YȆ$.$;n *D氾{ڪMa<+r/: Z n ׻6Q4j~JT מ|4ֹ'oLWD+Fӗa= EQtJ,u1jrH)P|T5ulۙJ$l$o) (k{w-w1/Lq74w)I 6tsTWG\WhǤ+,e:эa&dʭ'yOxkp#u6fi, `cx>%jw"O*^9~1᢫g _Hyrȷ@Dti=ǃ8~D*o˽?b9&%&-|7ɀcOAKsv$Gl7y >.UR]ۖQB1.V-vPҏe& ɒ`\MAxKqdX7JǦ_f ח>*u8ٓܥF:l`OQs*6H- C1K͝%wT>d0koڏOj Nk/F0 燜[^gC< M gI/E A? lQ0NT(u;+_\W[=Lv2 =z# XDf̱W0 w/ȓjT<_|ZD45F9ŵѫS(TԓVLR _r~kdF}i4 Yn^7m+A.52 'б"]'j}Nobveu=Dߴy%R 1mM%G~xi4>_vɨmX4~ A?PII^ՄE\xvfD!"wB0wWj3i7 o[w-fRaF h#.u5]B3 !EY7.f))؞ W ֟[Xgz5:'kW3K%1xʮK^l3EՃ2T rRIӔ?/ 7(#wljV >WK/uBw <Vc䣟 zg^ "[b3YIA#n:ĒU WVaOQ-]z0tpPd&*[g9q{̾ltb&jAoZHYd0@ЈdYT2L᤻O,֫0NJ^nV@5rv`\K3O &/%lx~AXm Y;W74kG0=1[d^1d]RBI7<_NO*4 L#PazWIy'0k(f#Jbڬ<Q7" 2#żs; Š.2=4G%A\iW"#:dUԅ$G/o[|51+ތgau$%罥{{fz`3G_= L0FvK<00Ygf'FXY_[7XF+؁G٠",8OfVlZ.q8olm83}~V s}^K"JP)q4owi;+KHϠ8'gׇ_Q2_⊕GQB3q~[ĚZ%d> ׀a}>߬iV nn60I /{'0jvygil FB1@5ա g5?Isz8',i:Hk)û4ȁ߫ yqmuVl Ҩ1CUX2/*0twHvpv-d#dN=(ϜKrםSvP @[yQ/ӳTWcylM=ĺOq'/UCj(rꇒ-"!x!X<{Ej2on38jӭ#"<7R*_]bDqSl46=V=\ % mkkuF󘏱Oc G(l-#'5s~H "(F P3  fhұ'5G0Tt'ܢ8ZOY X}s(`U[b+bQpt-$Ѡ՘K 9+)[27'+>Wޓb{K]:mZl६?\?fˏ3o&`ϠĜUb+d0/ASqs$G7c`.8&AȜ_&ud4`6uzwkn*DS@b`eoiQeL/][X+Dr&>VJ}޲:ęz/aimGs^s:k|H7sO6 E].0ϩtr 0B~-$o.ndY>V w\$wQ % Xd_hӍoƷ% dbQFPKúkdq  j>Һ%(M)̏Ŧ2u UY#ߧXފ&4]u~o._VjD}?X֩]9b|hP U[7}a§Q'\JUrV" ϶+%h<\DawC H?{C+dyӉllipX=,g-Z\fZtR75bSilW荹DI|MOm8DG4VZ>9 `?uq68P/s| ~Ӝ4n"z^}C|nn ok#hX"r-ma?W$H]wY}̏ԧlet>:c7 ˨P5C9*+!T3V JϔXior2Wx_͚ r7҇WUZ7f8(k<.4h^@9]"c f"SNHnEܼH%ޅ %z\a*?,$%X;i* zܺ:2x.DIu@w%6o~>g94k.O{GGbQVԣĀӑaYyFڻB*~.U =>#}k8V>錳qb M¾M.F,!,_mX*y Vj󫌳~J W֡nUM4vfféN@/}c(6s tŔ5D)R`$ܜw {MK !ȝ_| Η*/9qa-!Ef/ 8rö=p^9 aŸ' ;D"U/ b#Y t#a傛*6#1z>AbG$^4#E3GUqyǫRlV>Y~֯׎ekVSU+,EIЙcU;LPӠJ+.HkMb;vwXO/!z 0!YZ~Wi 9 U:>ᆲN9F &+oaPÎ%Hb;I~5&[ 9py)C+^! Cqe /6KsD!3D^,Dm(Ttl0yes8[ejQyqH)/O\`꧁S5)Βj]XIy<-W-)¥'xZ !µU7m }})=8uCQ\mL(SG…tze=S䌴vG6"}gjQ ǃC !7!dOD[0Co,z\C-A<&t>Z+ŘS%s`Ͼoh̦ G{vO%QnStНesX2QhdM]j2ZN{H8xrc<=ﭒAʌGc]0$:tGMmyб#Imt wF]˷,ɤڅ %QqH/]W Mgl#Mx$; >~odt#6SK7=+|ڕ4*m NRz6P0ՎkGw=W5Z"mc'N#ڔIm籃6m!35X18MA7w^H\d&H l<+*r"LRh˳qL;Bvmc6vK]Eu nЛeYo?QTic"M3 C(OGv>YA)c"yNXh[ͱE\9#R4ݹ γ^Ai$-QW/Kúf2BG@%0M,Zv5hjcvɺ)YK~1(n۳NRoj:%2K{Z!x˱n[ZW:9iwlaKc[d٫i❏qngQ6xjt1C"+kYg抌C9&AJnUc@'k0ʰAНO *$GUشVܞ? R1. dLn~X|qEĴ5Zpp~A[}ֆGr BPsZ&U4@Si^o^Bk.3-V<g.7rȭ+$\v$vx5#7j~ג0@"ߑuĖZGR }5H__.VH.JJ$QuY*fpC*..${bQ.d9`?|O_p-źxqP&f Քa0F6p5N}ԇeLLWXzE4ƭ+]m;W+{IJEo'4 ̬H|  iL=Q,4VR\k>ﮌ,EJ9d~(fI8ǟ CNWO J0*4{1VuC09'SO nZv2Ѧ٨o0y۠>j|gFFR&4@Vlk;3[Ohj觼~;\5\_=,wE\28)DWl '^{|AK8f8,\f̈́|4Odq%qvv{nӅ-ўEf~N 1A車2t,Xno ro7^wи0m{i#F[ "_$1Z$pE]WMah)֌

k|a=Mo0Ge9GĬPϚ1E ?,"7H7`c|w|hJ$XKa~޴D|ypn{\|s֛= a(V۱|Eȝ}! `$\.C+3rA9 ɷfwp[T!!_>UW˧o%/rZdi8](sq}]Y埡(,4X}T1̲yr~dFpD̸miL:l[w]pk/v85`PoOȼ- ,;|J.Z8{i5 5^]P-ǍE!aÈYN~MF=f<y$“MﱦyYՃ.eM{|G>$t!L9WZ1ΣO` ? ΂ˌhĎ}k|,|Lgb"F3׾;(eg+ j#ؠv%.}ilMFsr5.3^{فUѲ4KmZw^"39?fos?(`IJuxl߳[:I%Cg#`\^]ur|"ٿ|2}Y>X,[Tz@Q]Lܫӛ,zfV;rƄBi>w4Z7{*@Lmzz^]P5k7$y(y L"A( Vž2|rٙi"кAW$Sәnm~Ao@oJ?sTo3y#K1_Ǽ,H2*c_7p\Wɥ|^9~i S=fe"ASSh(.joFNxP339^[*J~\(iƲc냰K^4R.qal) t> Uigaԧ\:wJ)sacA}} y*?G3d=䡻,zx7 _eiM/"|Q̓Fկ~6溛M/?'sxz4^C¿Ώ~K9exQ>B{FӠY88<][@~ @8udG*^<''8T3e!ƯN)dckkmJAblv 檳k%j\\AIJHguaH-UDD&,)PXCꋕ qG3Fm H:h7iXGC#3G06WY DOKW~' $hC^#A$zWɎ@aN:5o_C1*f͝h\J>| a>V]. \<1>jU&7,(.Sh܂bt8Bx;n__'oH٨)I-Q0UE1' cPդ=>#%3V>,qn,`}' ^h_N 2s<Ѫ8r!ѕ j!*8]o)ֱ?֓)"\o]棌rd7'Ob4LĵTg.A>Ͳ-vߙ :^*vPS dPRsMM*5 q!A8*H%Cu1)lⰣÂ"A^qzhe,E&/\71{Ju[ HIl5T:l` ՖSa2hBXL#/zϰFrP\hs|[}'}tAVf6/sG*'z;##]WAģBlclj-;[o_R]t&DJ@CKu~%ILU.)|@]oMF͝8%zg&?rp{Oft(*0|}:M0N:yG+Gj1{D8. G1dgޕܪmfбjP`;Y[xIc%] 1=z{U#*DLt_uc5"vl ({:"8#7RpQ5r]6C_:b۞_8¾Ҫb=^~ÏTԍ"-ߐ}O ʊ>^˰"# mun\$<"Sg3r#?QrQ{A;gN]bE;f1dA3e]{ DIٽ;;7@,wW 1'sGXCl<%Z>1ڵ1 ƈp,o7R5wDV),p4p1b38_yVxu`=)))m'؇u;yWC>^/ ',A~UJTsNIN^YN[+m_pQx>DHe$8?Ը;8}"#3:ue Jع I(l?,g\I,Ɇ Hg/uV a\s./SeDYrGsز/:K%o~k⩭ L>A3< à%SUnėd QSޗ~37@]J[7Hh7؉|t~ _6{tJi#ףm9T%~&/(m=㑁l*.ݸo|'k QOaELQ.-#y5 `%ا4w;W-=< HZWe#f+k 2FqO'g/7@8fƌQюwӐW(L_ \9Yy<޷QIjc"&&@'v]FFfE0{NpT_9e Wu mWD'sla-N2|EV43h$;4Ch>A;ƚOQ1ӡ}Mq#*yz~O'2CeCXsFsWy4}km<%nZ?Ɗ8 %O3Ppê?}LȐ^ * :ְpCxLX,D&-&-.Zm:s\c֗[pΰO/ϯgY %Hӥƫwi|7ukMPuO>6)HRZW/e>C &$aoPcuqz(5ZڿַWiR;L,/H3_@6?UQ~YPZmdݏ!_JI ^f/2-"˧QZlvnk ^D4FLGMG#xZ6DzA@OͿGanòAf?vub߳;΅vj (1o7\رuN#⍫}}E@EK P,ٟȯL&τ.E5 %јzxe2o]9IB;&_XSS|EOȂT]|x(3˚ :;#]<3&Ț_ VQvV4fݸ~bqj{W @~VŇ_vsF3892 fU1L償u sn^tdw{MLjɥ|Δ/V6Q*y0WbK[LB:{AH@ôs^"=G: #'3""&2{G7+ERmRMӻN&gO(CpF-C_Ƨ¹g/=\vj0/>Q,%B5aS`DÛҥ`Rs󂮽 %^T[&} ϞΎ|_^aZWg&xѪcr f?hO Ξ/!Dy2A\pdGa;WG e- x6_2ytnJg؅}h6ERm0YIۉ)MƔ]vm1/9\;nAUw^8cZV+A#`].7G?-;{>i{ݚ@S:(]:c3/ƟG i|K bK[‘xZ62Ƕ]L5u_k1p-MwWbZ*+@6ՇY!bpY \yG jv[->(ΣK~0iK͊%E XK ݒdkW=~r3<}HLt F-il?>E4/ZDT~=\ǒjf ?iZ4.ouPПK*Uu _츑v.*i1<=b?!"+)CşYQb\U#tM rzI3P͊n.J -TU=R38#!xWt6 .BDX/8 "I"5v^&$_c?m1kOgPW)ߥ#ň^diD$ J#xܢ/}$ˎffA4Li䎩{WkDNu6D{HU;b7e0Py<|jN~5!ĘԕV.Ȧ9n A]=`{`t.|+ iC<Zw{H ɏY>ȁ[v@嬽C2 $*Rnb7 B2l b:cdrZQP4 uDEEM vhM{+G~Oe/ͼ@^Ns)\n"K慨ן^1ٖ, >M&NٻªBgǦY`Lf>>\DHQ4M N}BuC걻VmnU[A/^; ˎ:x|t5`/gJ>k I0\͓'@k=EOmҜXLR_ !Y>n.fy WANg({hC"sX4B5[mPVY]>g B {[Aha0k?U{AYl=haD8e*ˇӕ[w`}߆ċjEu9' (,2<ȧ{6N!OP}S a%WX p`6W 1"")Ȱԩ 2g?n{4jCYȭG9}fv= '[r>/!> *<V@ҹNYfV6:q<ϛjq鞒4k׺I4c &y8^;~~Hl=9G 3::SNp*cuJ>GOвܖY:{>B5"vIVO9㻊xgƃ؉`u%g6wSce2wOwx!#|˨@}yFArޏ9~mU!퀼c+eȿSUt>:iܬy NA=f&߾Tdl\ :kVx4!l<6氱p.奵g[AR͑QX FOy`%sCȇb읷۴WG`es:l],l:fMkA|} !m'wCL6o}ف%)ȑ ߱M"`ܓ׬ P1O@n>-jJYCl֫fkHξyQRMؤ[-c-th>qJ 4i!D{^M01CD1/+WTg'aC20C^KPGf3瘚вАo," ~=V)Ӥ u.z#%_+g?)ԋ l- ūT9$P`_x2u:R\|XIYhFk@-3)qC_?j* j*ɮA~pͮҸ3L-ꣴ5y:Ȅ&@3Z5M x1ݕLEswRhY b%]Z͘;6Pj%=Sn0u)>krܢ˳I~1={d|.V(O s*eN=%8ӓ]9b,ϓaY51:ʗ+un=((P[5iQ= > S}츜k~"ƻ]jd8n7ɮ psqV>z! 5upJ,.WA7x})4H9>v|lZMq5udR~04Jqr%$vovJ=b1MNoDKo'Dl7݇@h/GD.iUak`t!*h dS'n򃅟 ӭD:k6xk_t5_􉼠eVhZqN}uB-i#(~rf5ln0dosR?|Lws 4\D#8SMdgVOCj,ho-Y |qj[I\$aczIitL}0#2hUL/3ĺ+-Q gC]ONQ|I;rϠm y%5$Ѳ"-QūGj2j.+*?kcߪK5z".L!`lrù9 (8pSLbj)#i ?H":NI95[#+ u-ؔ+Mpퟴn`'!mz|zӝaOvap"H%6;<ċ/0>~0x)3Abtvۥ"fWowpRި9&vRđ&t9yDd56^uKUx>@OMyC[ݪҔd\^~mѷ?wYͶC"kf.mŃf@yP?J;^6ŅdBUM(O{D`*d,;F?mKV2FK\,q 'b?c~UlĹ@bxV mO]A @u)S=žen3b8R) jD B;k D񅄂_=iX1k 7nHS\POR)Yk;O.FU0Y-Uo*^+YNZYʹr:/!ߴ֕a']:?ұ[nA>΃5 Mrt"S6xfMcO&$t5 ހ RBqad@IqA0YQrE{6 m(A^hqY?&KVXZep{CAʤdg2[ )qƥݥQAZP`OzxwToa5kq^2tB-|ESYu,x8=\Y)U(R\ bmg*pt#CWʠ㘌&&vFk/AEK(υ7vo mrEQZA}(tt9syC88?Dݬ_!lAXT ?W(,YI=ҏC(ܚ)i!rU@53.բ~j"gB&B%R:kCD`JviǼ\`2?>WDQ0HiX|܅ Cy#i3ZEUG\CBeNݥ) -Ѱ\xؔ]ob9KڋjNB*-])މ$ivjTD4ZxMO-xo]{l+c2T/{SmOu͏buBh)^AWq͹`wzn1\ixm$XE6SEc6r#&iȂD!= Nuc:Q˲^q,z?2Rm&}@Gd n*2+xdCR4Xw>$W`tibt򏚟 9>I<$>jEpON}\6^)59Xo)gEs? T)HCH\p7[yZ x6JvMTlfm#9om౮2U/7l#wg(=^R!خGXE#A>Se1@>-Z[f0BxUi腞y `sIWd<-*bxul q,ҍ Ì U9'v5yGMrebQ$`BƼKDdyp3)qJ endstream endobj 168 0 obj << /Length1 1927 /Length2 23089 /Length3 0 /Length 24256 /Filter /FlateDecode >> 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 2873 /Length2 29786 /Length3 0 /Length 31432 /Filter /FlateDecode >> stream xڴct5Q wl۶mgvcnlX699wd$;\s]^wFH iM쌀bvδt \YY;[;FZ%#Thlag+b ;䍝AN8R8r>dΆ*큌 3 5RR?;Z;LKl!: E'Ks-v#)**)ĕU)@]8 @DPNET*7ȩˊh*2^ ttP:@alEOFgLghFgo?s '# HNgs 1 ;I_N$?@B8ip_m ɕQPZ:m mAΆ.Nlo .{6.dZ_l,U0f{,l I*ʀϖV-?ѿ ȀF` oD,@:99~ZؚVŞ^() :3-͌ =N@o S pvtz{o00v :S]/3ɿ]7TJ)5 09s?\ m+XXPJ!ghch?> '1 w?*,l}A[3k hO1>M֠=z,~?lM- R@(!,&B?av&f&V6g80<A#mtgPtvΠ7fB;^ЋAz v3^bKA ."A .A . A bG PC?N5 F&PG#GCcг/3u x,m" _YOmizkR䭉5:Հ E."#|6|caΎo&>^锷s+elFsFq_DYw~e+D ?{oZԩ*ݞi-W#Z,p2+U[v@dDU/)59ğ;^D*k"2auBllme;GdW̼:+~pm0)q2=^z3W3_9zq͕TaiZ :(qi| 4RXA()N_EX[~c2[xq:|VcA9˸(? bfe(: дQ楘CQ"&[$Y?ߛt΁Paj=-Z?1r6,(|l۳5= =`x5ap`es[A' O)vhQo{; l SK(? t0̓lLrlJg%Brp=Y/LK^h)=-~XLl}<"YK*z &f%] /r$VTe&. PԌ5!{k%;x0N4ycWkp+g@Y>ԀDRIR86¥$-8kWbbC \nzùAX{";(ۨ& 'Tc G.7u_93&c nB)3t emaOBlO"b|%V< >ed,`.vja_GitQӰbO#>Av q 8_1=/ƚ(9KȞ}\t5E"uMf^E ;U齃:5KKy幩!ԱF~toUz:;"cE?KTvl/UWژal\Nǒl'<!\cϲyA$r5uZH<ڙ]>lilMnd3X>bu8wόB^5Q*XU_3Kfu7cY%9͞NɬSa} ?-˕5ϧ#Uy-N+\6ÿrMj QU2?*;C<H$nfPFԗX;x[=/W4gmeSn{#yoC}Y)G*<,)>aC*g,l&/ mrO|Q{h :G%0DIVժK;ۂ=1aAķ)>T;D>F_i]b\U^ԑjRZ(>Icm= ; _n, CjyUZiEŌF[Qqg٤2'TmܹhZY|G/cuNaI5^!f: :ͯE̐qNF% leڡY>E뢦K,(.c$JH2M-3 ` r_;>:lُnsrh}l gy:@uVdHfy%2ؠF4nFC%M)6U-[2EL ͋xwtG%d?icּ>VpZw:}u]f; $ٵ%c 7N=ﺇ\zs:cD,OSQq񦑧z~} wETmd}S fԧ140%N؂H,Z=T2Oȏ$NӠ!Rs?2cH7# 76%qJ>j͗H ; `ow(<$\G/h?([=x zn ?۬shʬ;#>~e7Չ՘' ZXax|Rv$p0INrZ'e~gGECHMp2~ߝieOh|vo-=ُѽmK[T:ݭBI}샺J85 ,la\C-{iP pm4aX^!>q8 ep>>jk34߱G_*i$l<Z #dJ< [=qk}pL 0r41ӻtS`!拤ǰP^X i-C?4 Ʃ+q&[YTKd|~ľVݯT 'x%; ki72ъ.#/e.JO 4 %6Pn;ꎨנVOh|P>GY /rleAB gHz1AJѻg=w=˖-a<$|Ы@T$u\z1 %}_A+~MX0~#Xi5kju&PFrcducqTC{Jxoۢsa ONͧw4r yd`)@:Z9*e]RScaNeXk*c)=;m3ɴB> r_^rU,΃0KBJJ}͝ߟ@~զK{q@.N?*rcE$%ߺL e 6c6Wu5shUțV&sӗ$sIRZĀ"{bϾrhQɢRgE yB#c&l{\UbyNGz>ZYvH(1eO~e}}d߀qrd DN F(7)JRG0˺AC܁ j.4;Da UC S,{;ckjK?ĐDǕ]77DA VR`~lȚ9fzUZGñ:Y ~n{wb$+ٚ\!()s"&VDGM:2|Qv,CWFѰL_EWT?Gc.u @u!*UfxG72vX*خ_B.PsD{j}麭~H}a`^5 :?P1cp@`_V6ҌcftS^tjt+7U)1l?>8 ^orOx! LI_R^>C4mA#ϓ몴OKxqΣ7A` 9d9*lؘ MuI;V~Ejj<5 2:Q8#wǘZzZQ{S8S?@7,75a$-&x=68 *^~)⋞ʻɪWS{.//ܓ1P-򷶗d&EjדvEm `_}3|SOd7%MF.Ɠ _ hȢ_ݺ1▕6}M":H2 B ʡ:¹x*C 8j TFIyka~6~GN3Qr<Hx ham-!~:'e JXw|%D>5nEn12Nv'1;LYoqUl̵Zd6ՁHͽ"ؑ?iIE> ϻ:Nnq܄nl7  $̽A.gEWMEBrmҚRYU vP=mYc|/ą]lr]| x'{P<{FΠ:L^¾blc[:/Zceh֜ ~_WA ~آ'VX[LIw;WZ^rWP@,yđL`q.~iiGedn[n@+]|E*,:>91n5i#Ыc +Rfۃ5W%gW MO+zDi,i7a_!E#kkӵ ˧N1Rيc=o-_R D>m~dߓrTYLNqfQ-`j xUl 5aMS;$(^dK lva wO2v}d82**=7 ypyd#Oh'sSx""š.MnAFrG׀@Z4ܯ߮P$Z(i& (Adʽ(#lFhoҠV y;8?ԟp&X?`eƖ YaWXNGEDzӏQw=Dp9c쏍Q^E Y;jxGJ[P1VK7nI̝n8%أ]J9X.ny|RlsQٲk}8W\{TP^(4Jfg؛\Dt[U%ߎa?/hldi1.WtjCo|B6f_z +$[qEf.;eq!])g[lv+ؘo98hmw$f̓aMP\KEZ $%pwIЂDrSEWzEƣH˞=HTa>1B&Aɴ7$S 'jJޭTQAj'rcao>$ɌKQ,W#>x[6:Qm񤋠,+A  3 ^e-Y'ն1U J2ΰ.R4cf+O#*L*X$ցe(s) $˲ 'L'0l>R8`=*%Dnh1JeLCԂl׬kgif h!P1u˙o2'sd}\G1sK O:4FT׌7MG}jN]zx_8; T9"}X @#gZ{l[%ںF"ɪhIE0-w:^;?p1%UƠkMR#q.:}@׆35s)Qf.F@@/5* 1RPͺs3~|.@ՋZyG`S ̢63ӎD!042vFD W>F<6`ĪgƸ! ,Ƃ]$6-15Q β`zŚ]bsUk$Aޞ, yVU1.>OZfa$8AȈ$48ȡOmPooO7;}`Pk jf]\s'叿Laf|'hM`[`8WD7$ Jw{(cKoX![N!o%7Q>SY:`>-l5>+m6KM/kaG~SL-5Xw}l*!]s^-jgå:U~FCofɗPrMӿFCyK.O^C*ŕ8Nz?Z~E$IaMs.`vbF &Vwx5W5F ħHˑN1u$_BNPHwLKAp/ X5 }Q!.p{ać~j푸H!ȧրRm6.eDk)o]٨~3]|P@FA19LڴUݍRļyG!'/?WqJnέr_| L]N!qSo[2XdibXI\qEE]+6˿u*yUi3"YlG %r:/ig>ж ?9}Ւ-%G*'ؚ wLn9yg$uсw}qCk/.:_M1c_z<؋VU\4{P䒍_럎w+?0S;vr eL Z v qhNqEyB gP .X' r?t`_.܄[oG{\.K,.0ؒQx HOJW=?jhjN:/ď &@Qd!4/TU'rA ~K#BVzfl\rӏa${jUJpQxoA+$ 좗_i^i_[wiƇRbCE("Q@̞cjPha vawrds]&AlR[Vx2 bF6?VI6_LlAx^،Rd|}WM X͗U$Ǟ;QWz X Cot~*}-x{[_㗗]^FpYV`c^Q8pL"0žn-Ep,32KY1E~*y]s$ǣnOǥF$ɣI8S&MRŎ{\Ra\͌Ӣ`W;-˶HyQC?r5:thh p@Neur>}JE0ܥu8a_Ҕ^51$[篮BߧЀ"D VyXQzwvt^`Z DXMhqc&Zp|r ?BmC2% 3@=.R+D!߉HCV ;,^<"{+*ؠ ~m_KpmKL$]~KFN8&a15mS#LO[$o&%nibIeF^H{)"8⤲?)xz{x#4T1]"!Ń[G"~l&??rR'`x#H#ksZ=fVW0| L\#.֭%z`!§{*I-穉m2ؕkd4N^3Ekh˦g Sҕiv'$z{r;ɷIB}!Ƌˮ/ k?##Gg$/km|RL3NBq:-كr^zЮB!)3cڸ=fQqꀭ:ؽ\t3K<,(0؇v  hYDB}\vRfՄO\*_CPəig5l x<$jCEO6EzW-)ӜOѽQ4+<밥 h1|PǻEX\)± KOior ]̋UD ["lc̺;MޕDb pJnKKIY=ȴ(+k5 q`vkSIcR+<kwfir:\b(ǣO Kªt>UfPP%;IpȜ_9WeݼT G)ٞ' fvދܐk z4bv\QDiSBth <6KOxooc\2 /R9P$w;hÿa Th4KQg5&MQ[ Ehz[(dR1G@s$L$ۺp(~g ۦd{z2. D- 8HgR=Fdp@3x :IyCʜss'ݔHױ>c ŭV L0VB>&ȇՐ5OrNXL㶵c[8j:Md"Î%%=.6˝ xmZ鋭1 ͔۪0FF3s6c?y/ ơIbn;pgԂmM f2$bS:(y|Xetwk0 滅Q$Nhr8}5{Byd\s0ei@STy1d_{Ř8')-k .:KWmѐ[~FjV+uƌQ2 q:LGkәSÏ֚k#Q9<蝦J|Yc]2dG}*'4dODw?2UKwRZXnXI5=Yt<ݬKj*5F# k+wTcI^6DyX=91 a9{;vIn6z8{Jb>{F罆$Ҭxefy4E*Yl8L7F?.Q^B?NӒ̥/A+n(8We"rF_m}&ᐭgs{1fxAdd|5WLp6983ێi4Q|lR:7Ε0Q"k#̊ H~yB}aPK=sMaBGKqk2ݪ¡SBo ^]}Rsd%ÊPA< -Kt r*#d6c,O 3ĺmrFsF=EZI8Ŋ`MͿp 4osURjp v@0Đ:`/QNlq=yafuHC?\CutNi}n_I&}cɼo w n Q܆g1ߴ[EU&\{П~ѵ&۴lSOF8 ;~TI3s8#)x3LD(rܘj pGdn92sSXGnHj6Sp` U毐#s`Q߫ufyK5bA2l {+La;{cۅ4kW$OVǃOh=/ېE@K5^'*#7Urr+^T)q|~Rw$ 0zAjea~<B=E< QDur ]iy# v qӥW}F1WV@ȞDvG+x/#nj~WAf]w)KGRc-`⒜,9`CTnS8#3}}O&LU6*'ja)z4Ϝ}y 킚#m{#7*L·2S>: xjN}N}ƿ6 pkiT/-Z``8|)t+΋3ӟUS~ZY--2{>4^ n1'n԰-hD)i.L˃Xd@Op0[M#7[Ɣ^*£_l$I >m)r N4ҵKn+6K;}g >y0cڋFf( Z9&>2"߉ nN(f{()]*=< qX(,,/j#s1*?, NdZ 2\ow3LvB\݆i iΝ(܊.Z&$":-xl~bzQ?&ې=2_B54I !#0;=f< 6 8рKY]Yq^v zB3#39$0HfEaN}Hƪ xS2t.a7ܲ)|6vޖ@-aR~K:qrۀ@+Bf0iV9߷]\rC:6F$ݧ[_/!y^] 0ϡ?Q)Ϣh3ШKSd$NN&K9 WwZq&d!1 dJ;}46V*wؙ;Ŕd2vYB'821fOg%Nʱ}Gaۚ2JϫЭw.JWZ0T5j˭4{JŵG&(_y2p c8'L[/yFQd ya&۾{o\QHoB{_;/a0Z >-lL:Sx*g(3wx0h%EWh:\M+ؤ'u{vang` i0o'b2P7ĵfdP\x2orvi[@ O!RإG?2hn3~Z.*>r AE0jЯ2+!0t~jd&I&,lz^a*؋xP zez{x0z縲-Bu1n qHR\m{4V?ff.ʒw3IYlNr!s搰4*9FƖN<%O⭓YVH F!۵p#oK~0_4 diLS3Aׯ}uj#/~e$Zgޥ+9IlJ+\2\F,3+ɵ]e7˭` L[kJTgҝs bf8` \o’FkM\`#bIt}yMK='Zܐ&qBY`PPH$t(T鮯:u2CJ&KQW+byi|b u*Pa P+X~wfEdu7]`q) o?% ިsW{uZDP1 ۚiK6G\+AG/,ù̬8$]+V]5y'? $2]H+DߣPYIx}a9״p|01S&Ս9(;sTJ4\zIn9[fv/*"o 0X1kMDob^4jMf5PQPCkl hmB)ځ-.ӎPe\W~"6ɻԻ^Fnq܁zŬG{~/Xq ~%,RMN.=tx3 64[}c7 ptd*0h= `> _6R3o'64ZSJR ]VSsO?|w@x ֓&9fAP!$ݛڏΑƼN_j/Y*M)S`!IUޡTA3}m݆ΪZK"DuqSoLe-ST^"(cbټNZtBHϼ2hYt!o?|JG`&= p3,5_yiB${O^eWW$qZLdŋQ~)?cXk4Wh =\,"R26! /=Dw]E>y `8㉚=.fK?b5[SFߟ 2 u*YS]cIʯ^:I:V$o&\:W*@G76laXCCzO FR3Uv/}&^lNQkd.|]E;HidB,$|JFu$zJWE~m1P>m6k : r^hޯ_M1`UB91~즋zug2 jG|ɧ{sSt>7[D*s}s!:$r/ox$A+p(ZvOOݖ*ٯTZS|(+͑U,LLlDX]xɈKdq5/P'D˛E>31=*0*Xfgxm@ p;wuҚBV{JmCkK8^1OϠ,hjVv$銻uw$ rM~Sg_LO`# ;@ek<Ɇ Ajhɰ>pgWvCߋ˱Z)ߞ/QVki 56ӛJ-<E* 6=#0)!T)V=ɛG!e(>_4QE3tw 6`K 㺛 yU= 8,X3Mv 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ĝϫvMݰ^hC3n!!;ab~9պ [._3TU+KPwH2.Hw ztattpjQBK%m3{m.3`g4W/pI[}>g\\(ۙ$*0ݮӈz L.-@9u]﹁RXFnD41:~َE,lv)5e#+9 ᭿)& A|7bAf|)PZuaе2Gm\fojAaY?\Ȝ peMmZV.Gk7~ T|B[ݑry_UXb&ϰ11`j!Ozo q^e(;o&ڱt/A^כd.\ƞZ%aO6_ۈ-쑐#zE^ j=D*F@Đq/:u2m_/h!&W D\N6e\5q TLѓs/U"^@AkhJ]AW=ϐoͭtwT1Oj^ ɖSk^ec:[giϷt-Q֜v,>1$> BFsj1E%iQ9?G?-!~S&[T"kp+!/⩺s~bl0X PL9ވ cnI_ѧwOL ~kf͜[ٜM*09:Qk5܏Tch`/Yjշc&jnt{-ͥuf/G__E$A<ɪ|mS9&20LWW"Z)+C-_md񷓅֘O"f0GFQ9ӏ˯W,-aʗ'-3Let;0f!#X.v~PCS;^ |t< hZ’6W*Ჹ4aOCēXNm@[p ^Ln>ZF~*-&p_?`w?U, EDЭ]sC >"m\\3_2 5,(zn0;~܏IFUaR ~ fh 4O9Umj@OP).v+'8S-hgʮ9&Exj_Q$Zw_NW2QwOlc#x*oŀ z-NW:QC`Kϧ{ u]=]([s+xE:Tdإ+ACqOKʵ 3V&\/(q#?(8#!+u6ŃdcU4j4?8D=Ձ ez`Vsp9r=ٱHibI|꺍ǎ,5cH']?g-'Bب׽tP呣䔭uG3`ӿp-@OIʧөX響PF@ \[J!OoyElButfc\;Al=eu,d gL[W!ȅKC律_Bnq9 ;o( ܢCz*O` /[ju2ٖ`:8Y{u1hRm;A5dQFT:~tA/_c$}(vӟVViDp$ 6o4\YTO4pV 0-;/.vi13.D'&p:tL(ݟӊ}@|ZmJCP3Ħ;[>X.D訟~vld0HŴłN,,7s҇ {~|VLJϴO+cùPb%.9%dڅ*RO2]=m+3o %-gWHeIByȏ̈6d]P~4G@1\6^4TJKz{JaeT$5߶H),hҋ/yXU+م2:ewCCe~echcV*Wǟȿ|i.JςKtVNȕc@S]e_B+p){p`?1UC aTv&\qlLMYj4mٽRb[mN qSSuFvDD*kޢ{" DJfӎ&[a"wR!do_87]H |~`3>ě9S8t/dG+\`U^_$tVI҃9ie7zcpZ}G0pL't*ȓ~?6 bv3 Jk6^U*ZEǰpQ!ɇf/$jݭH"Oxr=an"E&eu|b<[s+Yк%ll1-;(ش52藁'k w~qb¾]h2+ƹRfqɽ9nzق}c9!e%ytܦ+-q8x qo[N(ʁ Z`4-T}Y ` sEIŪ]WywE}$TqHwdLD ="Ӭ&:wpiF^|t!Ɵ"$^LCU6IV 9PXshMn@Ͳ[4tK\ ;b7͝Lgm1o#<x#- w{돠-?H(Lݬ-+ 9+9S[.]gUuF#c|mI"<#$cSPM4!| 1 C;"E<(>;"Fos$`㕪O| 8֖0'gC]cjҟCtxDN&h#b,G, kb,^_Bj,w`:%<"SDlR/ l f!h3]f]ˠqf_1spځqc@y4ِ(i{2n4Sj}L_h iқj4.dg<{"5E#B Y6w#I{,!(NwxqGҤw*&s¼9(Ow2"O&|`H+>lzE O̓k+w'Dqk @'ϒlej棨,%yfau.O c)-c]^QI ] ӅD`n١j@\n+:Q_=M*tB_PN!C#fyʰ( ",8`}t.Y/ɛ,Ll][q&ar6{&mޓq&̵́{W2}jݎ=Ϲ>U"q~F>x%q W1د"Sy{w_Dn,Bi3ZiQ|TH]3.gduBn GfUge Pـ!կ]>噾~5f,+`N?#NϫkGU+' cy~Z^PWpe^)3z:\^{Rf*P``,2@C+isE1.cvLwN)Ӈd 0\_#k"ķ4#r쿄XX&UȀw $/ 7H&q& [EaA9[eeqE bHYZLJ9sMWg{;'@fsv̏i6LF0xgeU-}>8Uġ_˱>ֺyV*^ܼt,K hxq)@ϴ/_ . [ GKn4``&, za}<~!<\?V'fmhnewV$t/zjywԖfsec I<%׎\L~a*C> stream xڴeT;Bnݝơqw݂ Ipxd7s罿oSDIA dٻ002A fic[+S+#33; d/fpXM]2<I=i0]<,j㿀م Ҽ<,,]~`c`]w#@lc0703@oF+5`45j@-*@REQ]I񭰪KĄ@ zj@77ojjJ,L`~K2ioN Ԗ..LL. ' FۿYZ9AN6O'-Ƹڛw{2;'Ivڽ-_aop]p3?4))쌭]o..lo@3NN9r/Vgk3wuGow٦ {g+g+V_6yai qU5guǞF `~Rq{3Qݛjgz ɓcmcrͭՁI(-o&?6 t=L-~5+,oMv9̍mVogc7 O".ۘKۛ<ߔ@1y;f {[OI6qI*YzO"4R+mr)YZĿ3 [ ,l{(۷}~~^o~Nަ |ſdmo&Q yEE_Q 3+{ +'mX98,oSmkZL /{G99L¿M#7?$_ `XL+Ib0IAo 'Iz ƧA&?A Ao|&[ӷS'M?[z9#M!ǛHs+7?o7F?o]t#fJm9v ˛?*ٻڙ>``y[菆 (fy[ 􏦰ے]Ao׫?Yߴ8iotDz)o.RDX:)-ξs693cnouY:G[^Uru-39U]@6@M+37vqe~SYo?O(<yx3 Tӿ׿?=K S2?rh JZ2 P3b;@O_(?xRK(Bl_6[) `Tʒ_ ("9+>Ֆ>~,}:vJWڽxc`e3q*Qa Co7]Z֩~L^gJDIZ::yGź6 us#$S/dd(6tdH{MP;fxdX$ѽGNpJ/mxG}aN#ykqv6y*EdAf&rLRNƣ?cT\}z힊T=V# VTX cJME`(TOk&\N^2'ox3]S2RhAVzf-$Q0s̍S:Tux?#/(*l@|2U"Q;F5n,p! {E%,:ы}ѷ< ]Rki {1lYc^C69 9Ӱ% PnSG9 O%5g\t$EV8iN;32?T9X Jdnd;1u7 JG4".Lq$IG&k hؚؔ˸g ,SiˎQ;Z ks~u0{"9lc`bGi# C j̼XsVt6%lF/+dz74)oY] ٗ^Lo@}*̐$,n˫z\w jLإ(VWv.dd`` e\mٕLCW k"0Ú"z1\҇O()kIvK#LM\kСDojm/SVYSg[b %' ue6?#'Rw 39b?#it@caɣ́M!d 1xvjmyR#T.5+\몳>V !D|c=Ete.n._RiQRpUԦRXlo{i$eGfv7xj+ i\5WKRW=tz6(/$7e/K?^`?x2ٍoaWlr@l$| -չQvF`!MW!@>>= ,yC8qx4Rڬa ʸӗHǠaalFHqL`\64wұ z[dk'n,|s(f48f F&2P:hMM%Ħט.ؐv29nKX'U'l>Bk /FH3R>ϖXHgbV,}>-2? bE^wol\e:fTB0TmKF(}xVv%Y]4%86K@OkTR}h+w.}-t&=J~Y& >_?0>7u{R70`;EˈyBhFn^1EQdsL +9=KΣoeܻApMq14+)Sӎ1۸k6Nosf/պģcVr@Nѧ_@chrjr1Kxgp,FiFA}N۰.9u-fq'aML MuVR P%L|15! 0ܿfSuLN-yznϪ-‘a_|݈H6MrvC@lCu6%v8)@!ncE>mU189Uᇳ\E<;?Z Z=p;YDuUWW2ԭ1f5Ircxv;|Q.#>ҭx㉍V7g>)G{dl|,W*\]a,wL%Vnorі:푧ZOv,  /’$:?' A÷28(e.;4MZ" r4'yp$=% >DDG̑irɟ5HaEj?,pyyg;{Ž7DŽG\=z`tuR=&+)(Tqdj+mnBGO,'f[CI&.q6 ֺ^S'8aBNoz#}$U]eD9xQHSjiӯ&I$p˞ vJ͊/N?8ivUMHK G ęR[7j󋯀 \cX >];y \(ng@9qsn8,cJa);hfR=/}BEtG6,5P@;~zm20CALoQpܟ_@1v$:evICxé~yaaKP0XC(c'J*9憒&.§u_dR+(o BD_֪V);ֈo4@gaE|-R$hed9rPD-j]qtB}$l)y*Z%ه=GB_MIeMxu<}3s 7z>1;poQlC f$ŷf B147-U(!˟R$,)Q ercW^O.HRkػv pO~r]DcjLk4LV e**PG=QɋlU7-X kZ71+u>\Ea} &=_oaUU P"lzG3O;$aU?`H2yB@cuظ{3#[Reu0nm./<ǂKMo#~7;_`d3IMb#5j_w1ǨOWLJ3}8as{53a*"Ds^Y}|bD ‚BŞ 'vr*{2w vv԰2jDG`83_Cl r V4k%~0Z8^Vk=yp:5E)N`]eH(~wC)rxpuS_ v BL{ZH; ]c[6 r-qT7E{*w L2v2p]2^ ʄ?)nVm8}paxs !(dJ])/g,I.NO] XTzeg-`-I{[Fm-ĥޙEyBgɩnK[RpJ=R]kJݕ]2ʱcfS2n5 FI皾`))@&h$I x;fF+1g߯r~x֏"VKJ.fyYwgq\JmVK:&~[ʞ{'ҝOnw 3> $P2Ev~ a`ǩt rЯS,4feaCQ_EN+T,$! !".8dߏ5%`E|Q 0u'(U$\<7Յ\jLV3$. 'cfjmio| dj=)ޝ&.xN73 |)}A릆2LՊ*F(m.EVؿM*kKT`1+c.n Kul$X McW=ͥ1mJet6jO{aI(f &i/`$UIeDqqʜi!a۩G|Tmejh<3_>GCSΛ2$y@pAv'JC ڀ>~,oיW-1L0m\xNR#^|`xb9I$R9/uXT3s<.lQR5q`nYY:`=&S F!tr`S9JߋoDÛJ+MM6Lxġ E D"G G=BW`)ߺwhDQQ3TQt l+uK6IcjVM45x#*#O#> j!<԰BIusCTJlY+B#i;xw!lH?j .U\8&G~Ö4[pmBt+ ༬h?KղT3|t ևJ 3\ Yݴf:l$߾ZWGv@ۅMk4׻t.6>s1>Sﰘy7Oѡ]_83|\dk$}ȷ+b[ AR!nMҷX~a0׃e4R/(69іPk,!YhAmMX[|*<9A]kߤ>jܱmN® ٟNP5!qY5ؗ~&SYj7sے5zl~ ^w05xdznVQqT嗗 䊜zxj̗ȞyU/az$^܈A\~0دsETOytͦ{hs;w6 &ո5?RJi}IW`@X(Y?m#%gh%mZl,+\HZ7R~:[Qe>c[41,Jo"[Oъ||nݳ%r8kRob}+|16OJ?l228O%3 {2y8޷ANQDdPDOA|L[D8[DcJhMB sc?iFyŊ_KpGPY1I $Tۣ z'`꫗?/7a³\كV6s6CF!4jt4iyZXgGVrzE;a M6 IuY,H[}jmJ1Jiw2bݏ N*a[4kQV x/%HɾXYJ s <[fR:[>9Ue9Rwf` ⋏A- 2"' +%6CGF]!`۹X; ?{X! e2e Pz@v$e+UTHFXGc!uzexf~*hxq0dJr9Mb[멱 q>oCWat trŚF!,RS#ĝ4XQKtwhQjڹJx3B +YJm]W۲ּ]CڎJ:;C x(MtG ? cw.N)JUpO(`BF<BQRanNI&. ug`%Vx`s:7"LV4e ;!4VxՇwIc \W]4$ӎĿ,1Kg"_8< ^+DjKο>ʔHe3N}ZÅ-v J>F 5 n=eߖ3̸XٲV,֯e7_ذ5ji]$j(ڦpUPd2#>{{OܒlCUN/KQK]&hdq7*ĿlP3AlYqFE\2&T{2\^.{*; @FeE3$D?葃o}n" %= 'd[i.i)i~'_N vJ%I+gl o`yheVWqcPU7:yxx oAIGm7)ͩɠ:Tޚ~L;j6Ӡy2w>"/'"҂;E0+sS. ֍=t?*-p ^r,l)!VGꔲ!ϴv{TJ̅ 'WGc6BZଃTbSr]`GSs} Hl LT_`KWO,>7~u,f 3 9Hvd88gTB$(jGt `~x79(.J}z`u$F>xt6  qA D8$W#N $ v%Y~e 쮀ʿxD5@@SaQQQFc5GKo㞢z {q6A \M8qU4taΨ;=k{#ҮYexW<[>SB$m]gS2SGՇH X20xM4f7D/u{lWYL}%k2̬Ol֪eGeƳ""cCA=bn'%=ôެnnW %p@{K% H-TBq9^"$5OTDQ'3r ky@ 8- =ѕ͏nfpum,:0vhwq<3Z>U74d菝?ClIdJ;.bՀ}DhfoUmbR:7{%c%]amY3M3 "s$B+BxԥrڲzgZ #qp/נq")\PKUGjul-#/@>ꅹsJ_=Z*s`bÓ{]]2^'T%xNe%j2ѣX]öhts69,/8ыijv.mel&# Sj`qcFۭކ̔Bܙ4tدmLo !iSC܀He.f!+th!y5_SzPx(*7 ]&U%*qP@;hMR4,._jw`oQ*`ԏ8&gpcrISg q6^t.!l'mLbAj[Lpb c65{B$E*ɏ񫺜6GΡ̫wo5J)KMYa!dl OPmIoɋ&2sב|~D]˧.ZW\үϒԃ(gAj1aR.̃t{|*V WҠu~{oie=iE(TēItCutwwٌK|UBͻPno𡛼7;L,P,Ȭ[~K.׍ >% v^nN 0KJCJ)bXI"-5MpXݧ~+n﬋3&5# O*ti%QIt6] IlsV `iMfy?],#щe5 !+:>c/c7J&F~b mc_ \949CvϻX]N]l{ʍ}baJ$G(, >÷..$ytEm_!)9%¼(r$ "TSŝ8+⍬ QaˎŴ 6tnF/#ZEG.)9=HɏKZZ+g擳aſSv֠n@@=SܬغyjjpyK]0b#hΜKrvWFbА_5VȒ|rKlWS!{gVCk߂_ zB%NBbMנ d]H!8,Y,mQ3p`:!sN3BpC_;I$Z5el02D'8;:edGyz {5㩼,k8z51'{\JA}F%xZ֏x~ӰjԲJ&dEu^KgeJe)X7=/Vt95E qsCzuf&>hG#m 1U$gWZ%6^.%WQ?#.ޗfic=j|]#Hq(Ùx2E$Snǥy,R r r0?U REq ,RƤ"dZ 9(}/Ʌ }:y6z-PxNKJ7q\򕅒|a7\aYW@<Q}/$]׃EN$+WƢ05 daUX(mict]ϫ^F&  _9I`զ:x'WqN"8|Ķ)eC~x=.(<&_]D,cheު[zH2ew{)vbbbB9Dc:8yOf#n */}E6ܲ]OYAg!fɪo~ô.u[*+U8w7u+28}[zz齹7UY~-ʦ0LUNP>+b@tLg:sl߲3h^qm;`J)hf٦APw{E:q2-O` |ϖ Db^0yM .i=68gb< R-VAa: ~D $+ͮM=䃵ΗQ74k2;$ܯ Avhkƕ`"PdBww{ h;bCuy^}!ͬo,OC_ჩͶ̞s$ /82~a&cd1VA +ˆN}m[>Yg\0U9׉<YIתnC(A4- „҇`{yԤVͰ2 "ӋEM@9GI~ `3ifK ε0 ~oiy T[j5GIq޻~ʋ\!"M (dgW~]JR6Ub+gLc5gL]֏_^TEG?qHXeKK(#@ o {Z,j,v\æy f:E.貛] =vwÒ)7i xwٿF!y}\JU)İ)XTӁ6NY--u#Y2Y6⹙̑&\ =8NnY姼C//naL:!t o|e5u=%iٜ~ {M LC[՚*K"xCǬ!I"^1Lp)°|E1"mZ-rp6dċK-Mwp>Rd_ĶGa+r5חܯ z_ޯ~Rfֺ5e4[a?՘[2J+vҸD/B&hĥEOV㪼=fLyB3կiE-?aq) SSG{|>EqTsFT+|AEn~.7{֕!W8iIgejg:+sLTIkIdy \2seN+R y0iP' F!g/v0y+]JkPkVߛ"Kc 0LY2-2#{*Lǘ)2BנK%|;hJ`o `Mú 7&5p@oIFѽ}&~~|lH|Q4Lv#~ڂgHÕ-1* =()9ĤLxG"Q#9l ^WH3|߲i9zO$XC k<+I*G:BO*nyޔy&+`:V&HDG`!:`,W nAGiTQlFеuAhY*cԎ!J&0Ux32c$U>j"]-Ix> OEuFv[YSF@%[ʼnۛvu7&"Z8D& ј!Z撌n({!i 5MorY`MC7p̻9ǻ 4u%"'53kg/%UAzGu!JscL3+# 4Qر?%Ai$V\.l6-5룢i׽AG/I|Vജ]bWr]0NWvI{u5{)KeJZ O܄n G-#83υw)26PHǾ#wql]IKº(C˞zy^IZ} i˜@{ {~<29$ !BA/ dJru KTmRR\;xN Q8/D8֩I?=L=,S芝r-\MAl&twִVC#1-Н?FjDcS4Ip6(J. yoe`Z `9vTes;hCElf斜. &j~[7+e^#{}N])'r^sxWLG85{, 5.+m{Kfl-&! V5J5#W^n$[Z/,׆]@[y \ỼcT(!6չ .yftMHE˼{Aj R' r|?w"שjG)b:C0vjf*ȓ ?r9 b>R~w^ p%3s7c[TO,vq*~iU9XcϡvINWHp* '!Z/$E`u!!5~i7]J9S5FOKPВrx>l{X#7-emrrXjp/罍C$Dxp^Nh]ƯPzRf|='KQ,dz۫\Q'9}*1o[ɫx|,3mR9kේH.V@:LV:Id /a9& /QPauO2\c{t>&I#pp6S(lhJ۾8(_nIXY=//UP;l˩= tB0Ց@[hq-n& Ie  ;pQLƉ|獳x'^)N]Ϛ󼠫u\A|J%ﲭu],FcӚ?YwF!u:sҞ,ާSa#ED)ihH=N$X8{LSVI "VbEjR‡F<{3#v>GAPPc +q&LJ()Up2 |C=_`G !]2u#qQAS:8K f*VxVtJ -FbR@o:lSuz DC|-%+^^Wrgm"K. :1ϛaf"%xQ^ǎʞ42R!Q,oI~TJgv|7| ĩ䴻9rnƂjoJcdA̰W`w'+6?nٮP W6ؾSF26F34( ĵ;g*\'Ci+*`{g|>fxQ"RHJA8EmO`Hk]dkj%d&<*ku\ϯNńdqK B 4۟{\1t}['*a.`]CZAp%d",8B _;&}9L dxQ?tD.18+|(;6o լDك׭1DX Мv.It$UeeT!H$ӄV6Pg#݆7߫1 &{-6kvz:lDmSK8 ݳ'-[% f]ytW *Y25lSR߇sL"a:u&o(h=-$Fs>nR)DrPrO K"WOíM;z#[tQ.`bdr:譜5^B:Cō)<0x3<PƵiH牌rY0l+MXQ F3CbFcP6{{Ce(Q( 9Sd-l"qQRk#/Wzk}t_@;4Ȅ}+̔ fȂ6>Ek endstream endobj 191 0 obj << /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)/Producer(pdfTeX-1.40.20)/Keywords() /CreationDate (D:20210708112032-07'00') /ModDate (D:20210708112032-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) kpathsea version 6.3.1) >> endobj 131 0 obj << /Type /ObjStm /N 63 /First 558 /Length 3554 /Filter /FlateDecode >> stream xZYsF~篘GkH̍AU*Um9Nlv$hTa)VU^R%E8L(Ŕfx Z193hdF:90g9!LG`e$ŽfRq1pLZ1Sc*NH -piǁUpÌB8"؄Y cAX1k ',6gD!{x:83f5LPN4P"82a.1$L$*-Kx̙4,Rdr9k: rc:Qњ%Dht8t)ƒw?4#;쑌0% G hL` R )XZɑ?Bΐ?-J=flZ?y~q7[ĿqΟ`sM%666q9kj?Hm Ut KlB496 l.ezZ4Ͱՠ]̴OҼtDJ=YhӭVfxf~i[h Ӿ)֍y6}7>&Ͱh}k~۴T 骙;o fs N1cԯ '{Kkz[8zv;v_>3s6lc>qpIĸ OuΜJ$jOdhǵ~nj=nkF(#WeśLjw( K8je1IU!;+4c2i/1io2\hw!9u4=c` ӧM UN[` (T<ٴ.%fXw}QDZPkiGjF Џ:Aܓ1O E :A `z5b3wI7>cĄS ^:Z$]`x[FjӢl 3ڢJoYz -<8P ~[5~]<-yĂI N፭vCgMZP{~{ZϽۢ͌ZF55t{HRPsE;%&Mu %V F7}`@\k@&Z}]'i@q4v"m(k^Xߧ/૘ocB>(Yc8(I Cac( T閦Ez> Ơ(tM?ǯ4@-R-'--UTit,,7!0YzJCEl9p&4ݝ1p׈ ;Wx.J9%ף y>d˓h6Vn)i sXtR.Y5]˺\h|agOśr. bS7X;2$J4[ԀI2˟* @\=">\\m?wtװ ,qf5{=G'Y*zGitMYEetQͣETFKugÙ 4TKoFf}\?>}E%F\K.8 .<ً1!yɣrӇCw>}:ɛr]=(^DDv]lF)ѱ={ό.9`M``O`tUb1S/fH`;@wïN.~|^&fc](TџR{G84IQP`|tNތ ևO(a[ M4{PJyINk0djttQ2#i} 1N( |XdU&z~1_.ź$[bLUv(;w~xeT{| >l!m @Ljdv [ %zVm-wNOr/."f,;*\06^H٪T8Ws@A ezP*8v^u VrNLJ E6df5FBW[-DnܲDB[:V`Iܫ}m-&[0:vfS6TVga>ii^t Ѓ|U yBӏGD~3  s=xG>;Pe3Cل&;@a9-ʦ^@6-M%!j YxaHCP@PfCP0Pz rϠ6'a@ӴBٸ"~3^.EY~Ϡƒ/k*wlle,l76@IĢ) -UZOv 's٢ZW]ah6\5_<=t֭,Աllt|g H0 ~Q9Do völ@:{h.Wz9tnd`~;Q CKp[u@̛C2^-˕ y 5T^ KUniNwotsutC6Gmhv˒r.0+:?w=pͮU\9X3;z8 Tm.urb>-a1ߘ$@ UDP^L\j0a{ANYQt`L>ڇ>8L7Fi@*U_A47I<̲En-l$kx]䋜$2q{uGǺ;"_ swz:" ] /Length 485 /Filter /FlateDecode >> stream xһNTQQ0̀:7( *xGPDTP1$6<W ;簰XؚWZg>ٿdrHA4h67潰NIfE@8 <;0 %\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} # Load example data library(shazam) library(alakazam) data(ExampleDb, package="alakazam") ``` ## 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=TRUE, 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 hydrophobicitity 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 <- 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 analagous 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, one needs to edit the existing values in a field in a `Baseline` object, one 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, one 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.Rmd0000644000176200001440000001770114071626247017451 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} # Load example data library(shazam) 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.R0000644000176200001440000001255414071641036017726 0ustar liggesusers## ---- eval=TRUE, warning=FALSE, message=FALSE--------------------------------- # Subset example data to one sample library(shazam) data(ExampleDb, package="alakazam") ## ---- eval=TRUE, warning=FALSE------------------------------------------------ # Use nucleotide Hamming distance and normalize by junction length dist_ham <- distToNearest(ExampleDb, 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(ExampleDb, sequenceColumn="junction", vCallColumn="v_call_genotyped", jCallColumn="j_call", model="hh_s5f", normalize="none", nproc=1) ## ---- eval=TRUE, 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 library(ggplot2) p1 <- ggplot(subset(dist_ham, !is.na(dist_nearest)), aes(x=dist_nearest)) + theme_bw() + xlab("Hamming distance") + ylab("Count") + scale_x_continuous(breaks=seq(0, 1, 0.1)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) 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)) + theme_bw() + xlab("HH_S5F distance") + ylab("Count") + scale_x_continuous(breaks=seq(0, 50, 5)) + geom_histogram(color="white", binwidth=1) + geom_vline(xintercept=7, color="firebrick", linetype=2) 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(ExampleDb, 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)) + theme_bw() + xlab("Grouped Hamming distance") + ylab("Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") 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)) + theme_bw() + xlab("Cross-sample Hamming distance") + ylab("Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") plot(p5) ## ----subsample, eval=TRUE, warning=FALSE-------------------------------------- # Explore V-J-junction length groups sizes to use subsample # Show the size of the largest groups library(dplyr) library(alakazam) 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) %>% # Vount 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.R0000644000176200001440000000375014071641062016755 0ustar liggesusers## ---- eval=TRUE, warning=FALSE, message=FALSE--------------------------------- 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--------------------------------- library(alakazam) library(shazam) library(igraph) # 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.Rmd0000644000176200001440000001432014067611263017276 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} 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} library(alakazam) library(shazam) library(igraph) # 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 exmample, 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.pdf0000644000176200001440000077023314071641040020276 0ustar liggesusers%PDF-1.5 % 12 0 obj << /Length 1835 /Filter /FlateDecode >> stream xXY6~_!$/2%J!d`؞K2ض2:3b6:Xd_UGGϟ??-y$4"U}$x„,e O2)~y4V,<ڪ=l ZS CmlYマDR$͢9]pģv-v=ѕ0voD$(M%ji El!~¨vzfsﱎϤvoHOmIYbb->t-IX^CÇ4v훪a;C+qU.q5+DzamX= FJ@'a vh_ 㓸䢛} ^CM'ޛh`WW`ffIJ7̟7-g9UyԠhGEHN0נXɊFPDZF n!s6'q b_2:q٫+ֹJ4ZkyBz6Tyw)KS϶:~:ȫm9L݄v G``N  ZWA>8;^s76FRݱhQ*K20|ƸC-eF{Z<Ɗ%RQG +'YY{+5r̞(Qxe:ř}?g8(AcwMCSk~)|);J1(5ym@Ǎqyo"NNiv?.<$%3/Ԭٳ/@t\u6y5 Gȴd3]8YOc[Lj3D4rݑJ+`L-XZd+,Chd-76evv$U$i̩q ypyx+Mm`)p"]ўߘu_,p20VMCA<" B[})i}qYҍ~ad@fv]h=[h jԊP*iKT k7}GY.ZCny]5#d ,Kz8fr]yI˗X-KPx P1#t^w)m3.~UScYb]z?-u܃ 2@hn@Yi-@%D S$QdGXq“<2m,+Hgs8H74,K{GolNz3|Z;橄!R!]DXd|iVP-)p<Ͻ:Cv( ږٷn?ssŷ&*9:#l 15vC_A+Om;w7S[_յ;<;Wp Y4P!L߈-{;5 endstream endobj 37 0 obj << /Length 3809 /Filter /FlateDecode >> stream xَ6_Ⱦ ):̓I{b`, M[N4Զ'_uQ5} 4Y"Y:8jJ~_=xVA2=@M, gev-բ8;R5/[g]ga:pYyYqןWKn|,7n]k pS7^Y6yWUpvn3;K~FSܧȍۼɷEW4?^N83,df>;yȀndf|Y]L"owM2c7$޻۞cDlC:/M4$kjсuM-[7g2 0\<} ɔr*uPgtDMٹщh@XPtayw r9NPlEo*2FH `Q ie!CX/29]]~WnDZĤ#ie/n㕞od;sGʪ튜:j^0w&mrL@[Sr'@2掵T!zYlNzPAۉ%6%K%bUˢوu?܎dTԑFf%_^zF,jw0U/ IL Foœaldž~F&Tn)\ E2[ iclz`$PY4[3U~+gQW-Ξ| y@K_+w!Bm+A@O:ߞYOCӉ9Zߢ݊xfhnKsf/XѲ FIoa'je焣ܖOHEu xyޔuMyȀP[)x)v %do]( X4 g|SGK(]_W/H~xZ]\IİD P"1:(ײZe]j$Lȍg\z թK2rf*Z`rɭU4x>.<߻X;&'aq8QXCD?]8RMcC^QB؆JyQ܃ Qy#%2XۼsFEg#C?pOt`P:-z,l[S8lk޲ۆ\%ݫH*Ķې5w>9qG馮,h L"7V4NlWܡQ ,qhMV9* 'lr ̩зn PS.:=@w`XH֮| .C%A~܏wlG+bxf4HD6sF.KtX oA!sП<. qqqqB7_R .WI 힯D,ܒg>d%+$rGb'r *Wf;zj=Bܻ@8pO6kV 6oZv 9lT#o Π##Dsfo?~}ǿwբ# {`/lO H ix |}bՃ_ =,!d`DHd 8eoo7BLWy5Zڃ KБOM(WQ87B̿qfJgPMJ$mYУؗ6M~ ,_K{"kh#_F K?d_F 6,'(Ӥ؆ZJTϔ |նU >DCƎJFm$995g-ZUWIxf" d3kÉ( x \v5J)t"蔫YD;L5x`0%qM'_Ά{X3^LGqYB;CI/G] ^HEŦv[3l؉aSICaR>趒M|}17E^޳XJr`pmA'PI%FA}hpH* ex3Xʇ0Q8fD^rҋxbPH D/ S&Ȣ7xjs'Z{&{ ^Œ`e"W7X52F4'O@o)/0F%Q2Z /l G7=5(z0R;/HE-vBFp+%xd7^ itF qMq +xKmν]_K5jPoF^;rPxm4` DF;l4^Zr;w2M$cǝ˧KZEc.Nb|_{*c 4 c9I`(@eף,COÓX*9>ѩ l .0 ޴!"F kN)ymGП,jJz. )̟^1LvÙڡ>upV2lS/va mlO^xB>?cGߠS:$j5*DGS g!E=D?Bd{Xׁ`=N!ć|xӼuuepQ>GQ44g {x #:aM=?B$ٺh 34fy-'|kt2.BDɲGj=)̋ uDSsԕ2'!%X }O^!D/}H>a?N"wH v endstream endobj 44 0 obj << /Length 2872 /Filter /FlateDecode >> stream xrFd.PBԌ'S%3$S&"1|ׯ$Dʒ˕m?YM3}=;ã Y'|2(='Ϣ@'a|Of}6 [YxMZgSsM%fm|s7,-:ZΦR;݁o ,uWnyS0$;{?i(&`);d_@#YO)^.}M/W8\YVPWBVuCez.͒&skqv}wA2lՄ;N t@4;0,"'u5ZS%Ghv L9VUeUwY,S+XXZyՌX`~rYG0/OX5^Y]Ҋ.Ӆ;X^fmr!` E3--34YIdHcvUrܻJ%qR,C8, v ʏP#g{IԳ03P=Z?WU,s7EVvviNv^CW,OW xp -xӿObÇΏ>gRFkC H00Ð2|8"ˏ:LרLr Oa`OG9"d$1%f}uO ]szB2Y_[6S6 ?# %`~fKwc(ΔTQK3  $I&$.Õ aΔ#*L8 T 3SKC(~.|~5} N!19`eo~5.{b{?L\2-H VvDkՒt7@I2>}5)YOx \ |;#JVQ=Y4ghh{Iv9SQLaҬt̬tŢä*8]%D.Tώ\*ʋp綞69ec@\B`lO&P._`x^a]`.cێluAޤvyɶȬ+l͠ja'5AI:JNFS612"%iظW0.䥭wf750z+lmqX֐PÉHŶQWB"ÐCa":]J8KF:A*2rpδ8Q#!?U˽l*vqc4NcWdKF' a 1lo^q75> @#316[6Ժ.u]"7s5 Zٲ> `<]PZ&h`Z ёKI KzظG|ov[V>rسR[Ed :)X[Jҙ1< Ñ{iWz_+ % 2TdoaJ+3Z+3W(--N@~ϩ dMe Z6ם@wlF^=h?|3Wf48v;7)&S;7x.j]*sG 7eH GomhҢC6j'NX5XW;mFNMeKmLʳ;"Xa~ ¬\A,ߠ}簰rk;uvmk gq+߻:.mSήa0E6|lUV`A$o(5zQ?"H!/n}CE<@@H}ُ@ 2<ˉC*묷l'1 h`YN8ëɬUPA] y8*$qY}ƵOJ>[˺pJo!s2}6C(~RE_SMY"Q hLSutfb{eFI%7a0bq ^Mufg+M؊an:ÁpNA$~tιMU]JK{[ S{:~lvw%8 Ye-DWBm PBM$>H'(ֱcU/2ݞ8TL|xaS$U*?1$,0 :6Gʸ ^o0dKWZjfPv>6m} )+/=%\j@;+¿KV1~_;'2ey<>B`~ֹYyKIzk<; O QiUlGOV*̼J_/ nc%cveem~~e࿨Db!SO/7^Ϟh({ endstream endobj 52 0 obj << /Length 772 /Filter /FlateDecode >> stream xVMs0WFAi.i-x+  ظ8G&xjە%M.{vKi#<\F1x({w=Pk= i _s>w,,· !b xar¤13-byFZTSӢc=etƋLef>6^0w+}l{6$uvҎc?؈Em3MaV2,↜LJ^%"ɹ bAh`4MfBB=7qEWxFR; Uo.-TF5WXܛ3;_4Xг っ*kږPZV Y((3.yPr=d}%%0RO7k37kLiAƒ8mluG~T sg˲Lh`fHF5T;P8ʖ Zwdd.­4T#J˜o =PVB^=!xqjЖE]m"tW+g1vr>]Y`o|&If=@}N%$f||0(Lc! {N!V,-]k^oS ;DYݚ~McZ(-X&$k칺&YZOTwt9hQƷ7R>LA"'bsRuIܸnM پS`9؁j@e 4r endstream endobj 41 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-4-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 54 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 55 0 R>> /ExtGState << >>/ColorSpace << /sRGB 56 0 R >>>> /Length 1028 /Filter /FlateDecode >> stream xWKo7 ϯб9F"@ ^I /)iFֻ#јC/}vq|Z~۫s8>ޟ|{|w Ϳk.μaqK{%/,_ߙuxՙH;ck;j~BE4rX~;[pP D݀zvC 9CBSi^x2+9Ya 8C ) Q*ezPG.(u!JNCPUc]p%b Bw9<,/ V(s4u!mH}ٶ1ajy/eqN" k$_F_]J[`|o=}S騝JB" +B"ȆK>"NJ+:"NJ+6"džK2"NJ+."džK*"C-Z|kI_ mn[זo݉za-pI9 Hsm[IIr}PuUUe$Xiӛ/7wxC{ؠ!D4h ۏ|?6 endstream endobj 58 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 975 /Filter /FlateDecode >> stream xڝVmo6_!t 1W] bӑ0I,;Dt wgﹻ(&+'XH*NTEvϊ],g =%.v+;$G"- '߾BlGӈ; T T>b`8*!"UH7=|X3;TzOqD[LRDS JIeI~ .0f$Y+o5As'e=FG:XnJ@JaΚOs :/1YCpk]+JDw*죈zxŅ?{Hw} mv;(#WUPZD缝2! j=,"Db( m) 7<@܁pG]A LXj7˓R<&<iCjzHxr.I—r''X2< ߍ7G2cRRt,?"=D:.gE反=c?SW}\@܂پVJ[CL:V[7Gϯ2l1-{֜q`$Uf?i endstream endobj 49 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 1322 /Filter /FlateDecode >> stream xW͏\5 _#0}H*жX`_gfF=LfpqY;zԷ c^TK$L0FsE[:Ƿ#G Tdr0/1-XѿsoP>jsoPr)<75dH[-DS͖UBd*vɵu!{5xqy{urp; cMv?DcWksz<|wZ&HM LEE⭲\,9 zϽ<(Oӄf=FH4|ȧ\}s@^0F;<=zW_5հzW~ǧ_+'5鯸0i\rޔ4 r\cǮnF\|>Dk-;ZFnH ZE~d`!['!z6 Ѱ[a!_+M#">כ:ŮsF#e=23ߎyߟޟAR:9W?c; 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 1846 /Filter /FlateDecode >> stream xڵnF_A}kM.yH&hm4(hPxwfgHqe[rݹ/ly~=w"@ r!\fkˆk^PPOt|h:%] c hl&9c)Ō(5mrޤy kK@"W$t~2g*<"`i $KQqqPqmqX©0뤇ftJ#ņCAtJxa0a7#- 6X F}YUr2%B*B t`2C햧qԪSZZ`Feѥ[T9d*Qa <a;%j !)(z_m, @㚾9DԻ>藱r/FK\(< hu$!|u |}.l(&Cr_cMWx;Jh~W0u777g lfb.vu{ᔶQ9PxK3pz!,z<UPayW6Y[ܴBμ y iF;] _7}٥5$IE01!Cٮ2,AD.pGH(.vLpd]oUOVKk't nS~GDy}FfGw}'}e 9jPBd&_q)yQ͵n=А։~@6+\ T endstream endobj 59 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-6-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 78 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 79 0 R>> /ExtGState << >>/ColorSpace << /sRGB 80 0 R >>>> /Length 2611 /Filter /FlateDecode >> stream xYˎ]I߯8KXPTes!Y[b1b<1mm DD۷qbWFdTՙr=/ﰖJ>lҏ:HW 4<6PR XFX2AZ24ܩ298=`OZGSpV5"[)1 cCauPZeڂs9JӔyWaV:mph)BvkM=-'Z3ƆSr_J+J֙zvpbbD#C-֋@**1XVgYfTa"rmJ@Rbad5f%vҊ}V0R.ҺPWB}5`!$WL$`(PDbe"ߠu^$P5Sռ(( XI(\k΢ :G<&6آuQܾj\R 4W*qd0\L"ӪҚ@:!SM*\L6J<uς(Ė`buSR)Z FMA6wcBEk ocW U v2LJŁP nh~; scBp~x*~aT*4V!Rd}EV[o{}5V*UJiE8߱R! uxBc%K}N/ vƏ/DG~TLȌsЙ#%~:ՈAh7</dYֱFu?2$jׯ1FUշ@ @? ?$!)0t?$EV_W zKOsשG }v])x~(XﴎQwЊP{be,ܗ[: T ؈l$^q1n?9} s%nyˑtq 9#`ow0^}Fp'CM),vp}Ekg/q9BVVJ UpCٵ]UcEm9ENG4. z[Z@%@1jAֶ&B,xHqxh(:z'o,'q%H`箺K† `,.aQ{8L "+Zd `,%ˎ1`ݯ z ]3$ঢ!K0o"gk&p0ȧD_dy8N2Hώ ҫ5r׋PԛcUP#Db.Gy awD]pGk'ZQn`و=JxhCn6NU^3N2't찯.mO{_R`>M#XTNRN%}ŇT;þ`Kwq+S}u&aKQ`Q|i Bf¾z` J}))X`}22'BM̭:OVuu3(+`pþpl7\A)¾ fpC nX7>} 7&a_e3tܰm7ԫ fpù 5re3 t 'y2p p S

II*MU>/lY{=kn,Ծ!_EoDpww){m>S~zm?S $C'I_-16춳v3b똷3g{~s=4l<9/~x޿ 8ID?>EM{|f6~ 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 1711 /Filter /FlateDecode >> stream xڽX[8~_}Iı Z^vJFʥ ~8m3 !Dq|8{w{ؿ~zqqުa"Rr4FMŽ?/ ,1z-pw$A|"W"h@ bm@y{:PEߴ4e6sT@~ ŧoN҈S!A3ۡuEOJBӏ!("MUmz Q0JiR x-* \ll(af7E7y++mr翢XF˜ Wq[UGfQDD]Efg DX ̺ 䂓(H]ˢYLɑrlrzE"*cjLuQ7EѦ Vc[s .EW|vEY M8X7m2pt[,hj f) sVLIRWż.l/KV~ӘH4M@̓I0毚j[^h|4 2eV=uGe:|oVrdYo3K:BᡔBcv$!P k%!73F!j!`mY X:-p|AMCRHc%Cq8Tsd]2.ȢK)|sb4t&$f.mEZ<0$.B ۖ1` 8idDE$J ZEDfa!LكM}PC+ 5 癈}Y&xS&,:+VŇqvZjʡ;&XX3¹p5c:cjb9JMPiE kxDw!CUfhQ.*mס십q9Ag[+5Td|(r4f(3%˲;;|Tr76Sظ81MY7uqNeh|d [GEq۪E3Zidv3;Kz Xp\a-rg9, ٺ1vJ L=܉uaNll۳}4qa@MBOhنאRKR.K{nK"H@61Ku+F)Nse)exg"&C5㽥@_u0&BS^N$P\5t0DNqøĶjbXc;( U&0 OPi&K $RB~Y&R(#7"TE{Ѝӡ{R/D\_'Mwk6 R7/7p+Dns`]5* Ũ@dUrv 1G˯a% ߇T`pu6 ?fIC>w=!T̵ϜW9+iènci*ȘQԛ[MMᙵ">"r;pj{:>m3lu#k}o~?qƉ*0bxq8eRƠ}Hl|Rp_% 3ÕɑӜ䖪;c$1S8k'\PPL 껄ܝ': 36XakYUf{.1UvKBXC3i5c>cD endstream endobj 71 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/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 3680 /Filter /FlateDecode >> stream xKGﯘ%,22"E2R%E8(8sNMOvl. z2OWtx|q㯏/?ߑÑQz>K:Rg^?<~wz|±}W*^6_~[k?p|Hp|/-y11)3h><[s==ᬝy&8;bȾϳ+Lwyfg VX5lWZJ{^3/W޵4|gA&YilҎrZ,neZiIgs9*z&ϸ3ڼ6!pl# W:-Q:ĥwiϡ=o9f Ҝe]i"T3}MPw/KߑDqs?P]\"":;¼H> E<F4tSZ$"|FO2&J lF*" iY"9Dj@VI`bz!ӉQA.dѥ,F@"aplB:jiKN/.qt ث`ld!)N֝B-":\9 s^D3knRY#v{;C>&nS ІȦ@58P Q"UUJwh2f2<jٔ|_FT 0T.e38/ާ,Dc i3JwU*FPUɄxR1B&6%Ga~&/VJ%y͞LRɺQYB2˼iME 9tTY:^ jˈƢ-'E*SŒp~R͓*/Vy- 12UDJ`QIGl,3%Rg+)B[MHEqeN7+% S *c.XXXz5My5 Wy5y59- D\`)X.3{q?D&KgHK ,\d"d[z;u`.%tw"]zӃEjt`SFn~lJDҁMHA}l:ҁG%ҁ}إ)t`rNإ+Ϲf`=?aŃ.="]zx]zHA}l`.=Xlr.=bv"]zHAel`ɻ`.="]zHAeÃE `v"]zӃ`牻`.،vfl{.،wf9ҁс]:)hn29)ҁɁHAelF l`.=XK۞KxeݥvVY`圵KVY`+]zrڥ<6)h҃E *l`ɻ`.=vvxrڥ+ǭ]zÃ&+g]zrڥ;=9bҁ-HA}l lA jt` =il&[[Kkll."']:EN^t`Kr` RPd`s.=f6{҃.=kvVzՃpKVN^`w[Js`_{E}W/2ZWK}ѸZåmTߣեY-}Z@jO?ݪ>=sOj<>;}>ATK9?ۧ\U-*EP"jZúEBIvJ &SשVL0>H|l"vT"{Knx^*KO򩚶|NcB:h=]vԺ˧t3SOU4i,jqĦE0ȩdTѶ T6 P"A^L0Ҿ4 Il+=`SAU>UNT5gTuN\[O S!äO9}*@}*y3ŦN$XT`ȡOUL[SAvKJ/lJ /#TP>iK ñ|*H}b>f05SA6`{ ͧLi2O%;m>.Vf)ʺ Ӫͧꝋ|*HTx_T"2vx˧ZV]Te/}*/V;EƬSѧ|* LjX0J* T0 `So>SA;}*HKzg>QO[Bg)#]-%UFd6j؋0g 5 Em̧_ Ky,w>ͧztZ>d VE‰$R*6_TmZTBZ]S7ًh- d (+T1՛Ou5 RWTQtY*}f{~s|*S+duX1|*yhT|:mBSi@SUۜOK|*|*H])SA}*$TB&JSAJ1 SU[SAͧ^L R_UOUmh>6JH2}*y ϋ,}*Fn>(̧ 2J^Sl٧O}b/ͧ*|*j]T΋NzGʕ7SۛO]˂ͧb-|Ҙ̧ʱ|1TNJ-OU,̧lO%;s>ΑT|4Og+rͧ*|Rͧ*L1X3mŅSlЧN1Sɗ:HTCJ(KIb~T%YJbO%g+EG|*8`/@̧Ce>b̧gT|*z ͧãLDͧɺ-SAC|j>H>U 6DJ/8%w"K>3ĨA!,9!%׏]jŊ/e:**/j=^͛\ͺjNhH CfKv[>> 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 97 0 obj << /Length 1504 /Filter /FlateDecode >> stream xˎ6PY1ClEQoIa2m+n9r !=JZZz5/E 'V >oӗo1J0k_ &[ۜO~h (ˆ(>oVMͶ:Hjs\=n\z?Pc겝悳8jzcguY)6J5J>5و_RRm1:$nt K Bڃ +=ө!IOHZKiIwO9o82ѐW C{1r'( J1}4^Ώk(b=s|C E-73NJ黑؅vBA@PDt#ZKhmLxV(8">SmИk8Jg2y%]SfDA_XK#u|ZZrrzГE! (\('MZ(^tc=]3sz_$ 3Ƌ*yM1FB15vC$Bq˧KQV2~8TZ!^7^Ts'ʊ IMX5x ࡱ{?] C,%%<#pc`=w.EK9;r[:`&[ @h/:ݡM?D);ᰭjܽG=3^$od7!*ww?IRChZҨP4B~%l~WLN@&4>ζt>nʫwehg̾E2mZr>xcdž]/錡a@=ԫ?x, endstream endobj 94 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-8-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 100 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 101 0 R>> /ExtGState << >>/ColorSpace << /sRGB 102 0 R >>>> /Length 1470 /Filter /FlateDecode >> stream xXKo7 бEaUc$]m)@k9=:mPrfg ;=l.I/|SFCC0)?WiQ||m뫟ʯQ7ekv0/җ>O ByR[ku'x|w. uuj F$ 4p  0Fۄ>Yºf.D4ӈLB&F=tLh8E8L   !@tVDHR"p͓\y J3 R(hCS.XmQ@{K{)bDM͂牙 [wVr]sB^Yeࠤikf?[aB5& i"ٮ\k*TXem^Yt=r% neUиӴ*\^r\rƖShc]^Ǽ99;J\ZykO-@eꖓ:;9=3Zx.ۜOV*f@#Ůk슢֍sQt}Y+x[o|oxc'^?Ǿh59_vYń<&3j=RO)i\U8 je.j:f:*/MT\L]DŽ:\HFd<vÔW?|Nv{2g,V`F. t8]}xuP4 endstream endobj 104 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 108 0 obj << /Length 1406 /Filter /FlateDecode >> stream xڵWK6Cbҫ@-ɡ@=E@۴ĖQ=wȡdQqCa͐f^I((a2jr޽qqU*#FIIK-6Q)-$TgoJwmx;X4F{Z2f;1Iy=dՀ:R{m^Edjɕwvs ok:;"(d9)2wHɋtDxE?5w%.s0| 3#h2<]*3KJՖmfTk|k9?g<պݫ#22:>; #kPjGzUuej+UMmr- AJ렱mVG6 VWRD2MM#K™@XD-y<=WF't^䠺R(iؐ2sʪt.HULb@R㮰d471Y'暣7dUe\mr5 ߻2=42/`$ Q_iqAo5j CUAZ!E-4H9t=U+ms:d7^џOT 37i?%[\j9(sY^!R]o!l|HG05sekD(vtw(% D|YEEK dd֯~v~9A郭r5H/2g# Z9Y{Im 'Q[Ꮴ)^P  % nhB%`(Rdgu]*ȶMezR!4Y&[2Km.8=:7i2# NHKUb'Ylseo8Zfw~G \B6[PJ(s1iSߗkem8 @\f1?=;a=7-oa۫)T'mx-1;v8jzHd,rctϡ#ob}>-{ O='a:ڹG;ytCZ7$ _|FSUϋMKW AOQ(|KQGۧ Egi% Q" >_ w}lo^LsTF_SD=> Z*Gu\0$ҞvOl~9 endstream endobj 105 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (/private/var/folders/dv/8ryjx62x2dxfsb1zp8_9zl0m0000gp/T/Rtmp8Q6PTp/Rbuild9a4b3a973f44/shazam/vignettes/DistToNearest-Vignette_files/figure-latex/unnamed-chunk-9-1.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 112 0 R /BBox [0 0 504 288] /Resources << /ProcSet [ /PDF /Text ] /Font << /F2 113 0 R>> /ExtGState << >>/ColorSpace << /sRGB 114 0 R >>>> /Length 1422 /Filter /FlateDecode >> stream xX[od5 ~_Gj\^[qJ A+va.,Ĺ8' 0i}>;q_PR)jeB^?w);JpW Ջw*x ;;2ei%脵Z#@k]<>%ւi J.OAP:V:/;@uQ8H0U6J54 3"3 Qd1L-)<}$y)#f")`BL!(iS 9),Z:}#G.Dȑ㢼<.$3 i՝4.&i ҵإZa]hx@%'q 3 #jя(/ 0tO ǰiz\Sm3 efҳ\V;f)yzI+6be٦PZs m72"$Reҙ`{zE&!bL~ ;WXv#c3UY4MnI&;FUqvS(\e7BU1vSbh\6I34DZܹZJTUuqFRL!+LEickVXH1A =d1 %O,',nxcx}^;>kƍMǕ,w&U#yQe> 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 119 0 obj << /Length 1120 /Filter /FlateDecode >> stream xXm8ίJ%8 U[eQS[)2@CdKn0y21 ?'?|r=Cṷ̈̌Zc=;;>1qF,q٘<+P"Sr}ZvZ3\; Kۅ e?z M |/VK"4OסBT!4J3˞aώ~:oRTl=\ӷ')ߴbNkqWY|&,rK;@ȰSaKkm8c&$I#X0..C[W能drkn&ၔac_HL~_,纴MqFWU|f?|6#LoG5b L,D"y/J)hdY9q]Sw|ScFr 9k^Y,|H e8bC8I,rlA1vNW@Op6H,&is6脯B1U8,K:!_u3jf+8g$f^ pC(GAȬK*dѤa< endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 809 /Length 2018 /Filter /FlateDecode >> stream xYnF}Wx}A'nY$?ms#IMSM$jlkĀݤOթnqL3<3U2=sxp`REsE7D0Lrn$LJr$8MIτ&!iLo"~CF] \24;xϴI([;w)4%Rz͌+2+f0v<1N4䊵%Sx3 "J/ o5X%gZøq$k nq([ }N䈥@>9Ԕ>i I"ߝT#- ᅦS 4Np8'z;HTUoX%?T+3giўop|6gU%Ue5yS-,o("cg. jD݋- zPhvZߵ41.NE|X\oQ]U3ϓw~Yμ$RBBcb$5RA eki0ʿ`9:%ePwÛ n'p2(Z8X*y:F9!T )Lnh7%6.~ BF jabAU\*;XcYY~P)[|~E r1RʡZNHJ`"_wK,)1CSS:Mpߝ*$鴠xO&*#PONY1kڎ̗+bṯMI$V0j'¦:lZ> 60 ߐb8L)c^g×`3yb 0cbp\)ٛql9{!GVͶ},9F޳Ӣ]G݊f,i9 VE]Gå+:E316yuc>EFeV͊t;>&NybQ^]?;(ߖM>)./s@Eg\48'/6-΢:UX}U~Γ4[yuwI[gyrfuU&uLI QHfL4EfeRv!tyw^/ʫ^ETWUd)kn,_V#%JiOMxNQW6lX_YZWIz,sJL88-sxVvCsAx1\b|e\p%8ŤW1…#6.,2zYW{q= ʅ 7p!W@ }쥛S&^ RC^Л"N94/^ z>&1#!=0:vNLi<ʭ'U6lfaA? ac=rpl^>сHMY;iÈ䔾w  <"n'x w'] ','QRsC-5sjrj>ṟ^TCNsF͢T[PSRsjDgkj.uC]i(Zz+^?W^*.Q5co7o2ejͣa@˚JMw`|-)8) —5Pcyof|U1j3誗Ƣyw'r1N޳20Dz>f!ڑ+U [Çr~6#@"p=G4/a)5.~ 0K!H.-xeDލ*ྩ,Tvnrj endstream endobj 156 0 obj << /Length1 1952 /Length2 22802 /Length3 0 /Length 23965 /Filter /FlateDecode >> stream xڴyeT]6n P]'@pw(P݊wⅢgޙyfVVrz9IPj0Y8ALl̬E%uGS+J 4uqI<s7G0 oJ @ jdКT]\L]@ H"vO?ySs;G;) Ϭ PvxhA3% hiHkdUT5k999 #@RLYS fhihy[15qWSbcvQ1ڛ%ZkWW'~f+7WfG_4m\`; U7[9]%Es IJR9]EbGkS|UU6 W dfj0KZ  ɡO_iI]me>~1Sj6wظ#"`icOl@ɔĔ夥44ĤV_I*xYl|ַ!YH8:8vAS>I::Xk@ Zڀ,,T͉E d?7ҿeV@W+ z[Iפ `ij]|\L݁WDHl< s׷!(HEY:!~cOMJC-A^ %0cK^Ho+S{/EhjjnC.j6b +{[GiGov9Ll{Es;7[ [iHj(3׸e$2wYع` sq|FאXAo.'7W?#O#,bD!^/'E_bofio`1zә;ڿ6VV 7o[8\lo- ;o-o- 7omno4 z gU^M7N+cc7%SWb{=?PL'/7 5צ~?#.9 ڦ7WKTR1U '¬ftKQEZeS@T[m)57jLФ󴙵V+) 8IZ'|=S1( +7 `=JZpX+]D+3]pcLVM z0 5?4.Y9hЂPMw \<A+6xR %[c/[FzXEH Z "8RUD pMEbku`oeׅJ !Uny؅x• Xi:_$ x+}wG61k.DNlDQ-my%m,jV\=e(t/'Ny;p$8wӥ0?u r9}4KLƦ_UA˚.O/{S5ijp%w?:lJ#:_e7jcw1hvtNľ콓;ȇp1xhsLįR$橘/Aq,3kN~uzNRCc na.s`R.Qȳr 3vJy0i,wvA׸Z zl+EI3T{H!|Fd̊ ?$7M2iHDo\ۑk_h 펌4 EO-feZ`iɊO\}nv5hM09cEn$iiQ"ҷŴ"|\LU~MfɵqY׳bfM N8} T,TpfPHF5RXhBaV֔^?L\~8Q[^RPbehvDT'4vRcl;ÏL7T-#@x!š/u)UxLHmrgN|6A?ʍ<9X>Nl_Ğ0`47 UͷZjn)պ $a=トts].s9Bt {7rB|;G~y^jBV] Si[g[tI/|7أa챋RtD;5&M[VkB}}8ʾCjt/CN/^uK<Ca|6x/uC7vu{y)[(_boo<4 aF2feq"9E2HAɞ52"XH<:ΌrWu""|(1z}Ag (}2^i4La5V{ahaJO>do%z"Z4 RnoѴ0Xk?Z:>4tSh 85[Dϒ6 MAgA\姮ɝ #]e7n5W4ႍ{.!U\'}$/;ޤQ1XP;$&ٶ{):~AaY=!d`zcv,l]W+yѺ^+TCCLX{IIj`aߢi_# a(JsBnzeMl`n5E[gޥ&a ̌> $V[mgn:-NQu!q[}9Fz_E?mzY׍Ad'U }YAFiA>!Ε+!cGjT,<[1Bf{/B6x+U;3Q' tvntsc T!aǤ@gG=CNV\]^ UJh>]pǦ[9$xHCa;{}W@[:}Еj |=IȜHwR:gJ SɕմVc?xfFJϛ]]stx1'izdٔ b9?'DHa a¸,H{ʾV=B`(v!|ɵP)xWLbԤa1{S9E-xNY蕄18T: x)|K@(uOsxz`Ŏ"+i0ZY70sxMC:JKcJ MU#p=1 Evѩ@K€Jhtpʆ2i45G[? y9A׿~ZG~.>xS6Dv *z z͗9E\i$1l|򝾪t'٣b;--3Bk9w=ĭŪN ZZ k},QR,c],ڄj0u9|_Sc \ZE0|PeE%+%IiIdP[%snA;pdn|8xgtc;)$|bZͮ,.Z'ǪDNZjnϞz]~G׸.Y(l OL搱fBW26_&m n->tkNm`)\Q)ѰXZ? |'pJ(ZR!k%tY_l0@y6_ ܭ XcU"/)IoHg1g ta7r:ucsJ~ovn/H:nS: E_2 ~@`<ʊxHܭ$#zUD'mFb+瀤xd_퀾ʗ'x ιqS%VR 4, ibDq}riRM!5XKcύ{Jm@$Im˴}*y4VAzTW U vVpsV3ֵ<ّ] A\=C7l/EZn!ÚIYv4b 0) PPmNF.Um6 fLٱ X܅u=-kialeB:2d T @Ue4,?fRݻkaIk+kWw~.lS"'S"z0SFhࣕ]kFpg nXaPaopGw4uJ?U?vqLegؒ;NݩTH][<]F +1k[Ė4~r <3ij*#%:$ a5u{9" 'D omqp۔wck:?(eaL JZ;Ea1xL藏|~?Q'nwRc{ N(J;A~.ѡbfK)Wfa*8F[ΪB<5quuO *Ny)`np~D Ux<̿7Zܱ8~T9LTVaZ!q &,Ef[=P8C׫·(?աec~Av>4E ?)ﯞ5(޼PI\ϙmNQȯl>NYx/~⹶}f U m5cT>4 V)vϲ#᧲(t1DvB$3y8Npڭcmc/릉ؼ_0*?lUQ!5x;~w}QE,;jI?7PB黣~tmHBȜ>ȥL~ g74oHg;wnEUy0jݮ}je5Ow%DRtzN IhJD={mVnG1/^.Yr B0Gme:d-Z ^;^ьiXp.hsE>]f  /?} JҚLbq7KhF[%V?8ۋwVrQlw,;,z"mRk T?b"X P?EC/njffeopRVYZlV1`GWS@H4N'Ѻ5~frz9<8 Rd95A}a/6lHNT-:1aKB1pxw-يplQ<~^N_1?.rx]di1ay*!!gwyԏcF9+Fs_)znz- Þoxr-ç(>S.w{1av^W=߾n`tDlADBDHKI^a 5e򨤲pj;#פC:tY+2CW,w6k^U2y(a+>b ,ÕzL4-$ À;ܹYR2ϏBw! UsqVK5_v|H?[A-u4)2*"-2P#WI,9KtZ_~,Wl$qgđ 9/Y \=xm7x5NGU&m;狻.E db%)8zYO|Y`INP aI\n :{pi/;'gu2K}1JS>SexDXU|8 5hR CanBi&OR.n Hs@4 6%>BZ$xJ>*d߰v~b/.짙qt Ůa6XJؖlu杻FIK~N:f8'8r";[']3Go0jieX55 X*"hsђݯ< &)F*"S<.NUi<DlڥU[a\+ߝ!踄(SN阓mhem2r-qj+_K~[8TLJ|+fB9]mif  `~ZpMTvPˈ[N-3A^֜FP<< RA x||Oڋ#EfRčKo8ketiÌ,/4>7J*[n[1!^V`J#DK̰8Ru,~DLSU!ٰ̼cn^`;@;UH/]Ea]a t62W>;a;хuӈR<ܗU{V2%4{^{-f` 0+y3F Tɢ(6}7<\-bu8# AHTutyg@.TnkV q.P`np">=7V ndC7.2+ݼhwE?E /?,}s]T3/ɣomCHӁ˔I$߾@EU#& 8`<'zi"5f%U0$~?2>ͷw`f˨DfO7(ûԳҊ"eѪ5V:}z[37!'jf&GiVmس%TzX4<#6&[BGs\>/SZDю. pMEh5&[-* Zvϩ-1œm+k`?Qإ zKFnMOi2hn.9qmboJ=“3$xƻ31HC^M⯽>oي=a;ͮ`)y]?ƴ!ZTU(*#;leZ[,/0jܠ8~G&5^J_J4ml߇mG $5vu#̕vO<7ff[JJ@H^w[,4( WJ6yiՒfPڮ-^V7m_%݀'JG;FY;;RD,A|Rɫ>%3m]z(2খϮoH> _-Vx6ZKQ.{yXZ<;seo!odA c1BJW ?Dah4K_v$J6u%B {Q jUQP,IbE"))a+'}mw+ Wk.AoD~ޓ^P'hF10A݂HeaB)<Y1">=fmNuQ). )~JO%gJnuڜ%%V$0+^O376<k\G k+W.,J4}+]=-nr!jR8!EgldH*͐;+*U-ȸȚkܵ.6T~xp8ڼ}\#7-,A?.Xs򐤾Fiv LsC 55G }-<5@% )248QQJԗl=̲kO٩;:BdO0ہ)1*Y?+kFdZxJz }8; MԪ[tMN'7V8dECL\VgވXzÔ !/&Chvaʫ@ZƸ12%bt^eUQ4cQ Y=fʼ, V&6vQM璀u6?q+G"WXAa沞j1IIIkFP jS/,cMXw=.Pl;=m#V}fk'թ4xmɬ6뭩wt=+BӉL&™bd̉YK9fG&%FĶGY{DF$uOGTSFɵ/>~RZ"f" Y{gjc 6Ru Ril"¨SL weHWj!k+EB1Ymuۥ -+(>fQH Mspp m̌8:؛CĞgy=RJradnlJ|'upq[_2Ɋ^9ʡ;\U:3WJYaU(%Pjjˆv*`ym#q)Jü&A[MDHE/i"4>Ozo^wGx͗dNguci 6Aaҥ˹IQmb9Bk&z+Mx!33q"~a@OP7UGNI<>Zچ_yc8"-<=|d8 (O je)U pT+^O٩Y. ,:Aftz%n(JIXIQsDSp;Ll֖ƪ2:>H#].~F&kУrz}+w`znu(FxxW%FwR"-}u,=v$AѤ~#.&+%T(>7bA!ITH /\r6h 07lNSXfߏxkd@ͮWc2J*̥̇LoK`,ogBj܋{]SݧS4S#!mMDy[jS#eşyd/ǐFHbw1+rOΪ, B80zT>*RZq@PGt3jy_c 'lDzΚR82f;c|hgHB=!bq;Dwl+;~LyK =тPmwVE9 dct%$8b%Y,.D:_  R}R0?}I-@8UM/~VGEΜ4zwGJ^![2TiJ;2QA"$N2OdݐϜ̀.ffyvpt~9՞轤َ}6Y> ihˌ~r gYP{U:!lZW m`n`V#ѧ&.XVHojyԋs k*e{DRQ@(;gdDr,&${KҖ:гԱm)!3znMS▒"J-e _ho9IϹ*i*z\4ŐK9K$1M{/4Wg~4(nl^4#ka_j%|0›SrOܘ} ն`G u{Yp|Ry:[$5y/X=oY+΃lJq* h)F}4U]fh7>ڊ/1,$~n:L D.']lIogF31c45Y<Q}aAy:I~fW_Y-gPW^]v-Ԩ N*ޞ{u()M,?~bXg~p%scf[<\/q &1cR8jW(@~/>i[ R^8Oh{;aSni&*쪦}"4^{CƊ(^FsDVһ.8~WEx4,[`22 1cm֐d~SZ:(Ry{r"a}}la.7eK&k$T~~qgZ"(1|%pXm.)*V,zPqn2ȵ*q";W{z|D 7)#-~$qMe:4+U; k$ we:Ŏdf9*&j1Gqf?AY?^!飐OfgT߉ 3fx[I mĄvxU-Qeqٔ)Rc;Fj058T sH!v2'ǖE?j Bߵ1ePN;ì8Ut%'q`L N83Y9ҼG泍z dlU>Ԗ ^"x`MFK)<ۘl+\:Ԩ$/ &1=N%Yt5P"S}4QܩxwuӢLyk<o:^ QH4+J;za['MjժGeNJu󯼶p&0V8 K0EE%2.H Ish[+'?ixƞ`~  BHΏO{#j 򕓗t RYy(D=*0Vޮ;Cy%x{884gLYlWAb+"gs 9m~:yS6 J{: a'JM` ~6aΣohnUMgz:;AE2%2˗_y6a-*rÉv5VOP0<{Ѕ.%ٷ1D֑;K$͹a:MU&ڥ4֯ LKPGO?\NQw9%'P:F|y[/ۓDdhV6Q6OMc>/kVNVpiսU`anG8N d U1"Sݘv+K+N˵8[%SloY&]{\#UBY, |z5J&3GxJK˨^^Y)*B=6< '3ٖmWQ(ObP hx:2BI>Ѽ`JlzYՕ^kAv B+hVHԪR5@{I [m-Uժ݅V"+\=INW67v&7gݏI^]!Š'?GP/JkStPт,nGƁ㾰LZׁ/`DY.cLٝ ~a b.Y6fy4:VhKŗ_187YGW6;xl}&7:.LYx1b TݿAt;|U}xm\'yu0w=y_Պm?,74àr–*)b9Ib{|ė @$ސdH CW}F~(P?X 3O$)j:HK)B3>4ɝD2Y #La:aeL)EUJQ*=;)TɓĶn$he;5T=i \;b^'&'߿ll|b`[U$|m3q+7\Nؽƣj7_)_, dPc8FfNVˌvH*@OjUc8+D+AfNͪH)#?nV[ZSֻ) d~q]3уs<h-Vb  P:?~#YBnm؀>u&႖]KHEa٤SSi19b49!c 4pkQh_ LVhS =հ㜳htd'o9\ GUOl6d9Q O9M y"bZC_H\2u|E9S|#6;ѽ-2ޓpk XVD#ǣJ{96V?WgĄmAy絩{PQk H~)Lu'o@W9*O3z.5;K/v rʲlA*b-\{ !Κ }Cflf-ˌy}`Y؂Ҫ)FBvw Nk&0zNyVo8M*l L_>eb "x創G}.!ȸt#8*yR_jn9ԫ2ܿJQ8XABj!"Am ua9x%4b/[{N$N yÎs*ג5n 訧 u6!Y%h6d;%TC ̞ťTv`^oa==f`3G7U7*.FGC]l_pB\D8yQ̹s#wD l+AC WKƙ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 6P o3tscxupHde9#TmeƝ3MFt@qaY2phߞb8[O.WQ [C~'5 85` 82 fyԭ1+vHVhiOB8 ]4Lxr8.(fT~qUJDw4ń8 ݰhGF(T*6]Qۤ=K?k5BM羓ۅsfvuhn83R]I (nK':c閈"|La$ur(oJsOȓp 9Ao&)0P[.A4Y17qhdX,e˳5f,.s*Y61$lOבJ?FM[(=_H!AĨ\E}1{_t`ַمlޞHڟ1T#{rTtтz1<@ga%t"ظ.wyHZ,q@+x4%*_"UuyoHwKu`5O*Q/5!LArFdнsYiPDA}{^3႖DЫj뗧`wv,"T̋)Xo>kʭX0T_:#JkLRcpv6Հ,oTwqY9N.y/bW 3/i֧E z tt%RM,2 a{Sڇlf\.rNIr™|t^zeKnv%yZ2ϱ,SX+;H5g)4q8,?q꺓3z!sAUedraUeRz H@x|bzvoVʋV̹2HFoXIy4R6ՂGn2P:C$%)~6(.D8gK[Sc[J`\vKv} +CfILɁA@e nY+ }KMVĠ̳-EYU|sn_A-K3u8 3YD{v/~HP,2vej1:ugl>a*16upQf!Ao oBØ(4\mK.F`8{.V&')Ja͚MF{RCɰ|5H4>¦N#0 ٤ap2Mlu|+&ڐ>kn2)oڒ*=_tsC0^pƤ.]w~ʤcrlww { 躩Д^cy [?yGJu@;-s-s&zĥd'E/ %q.*Ki`|F|'2LzZ*dWrvH{A)5LanxaQ/F/O- _.6;AA|#Hgz5KxĴef_'MKDOS}4G2jljSgpxgQW݇- endstream endobj 158 0 obj << /Length1 2072 /Length2 23426 /Length3 0 /Length 24667 /Filter /FlateDecode >> 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 160 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ڴeT\۶5wP8݃k 8I <=ws5}h}9**2u&Q H df((LlLj +w{S3++" (apYr"XYy G˛` Pjx;Zӿ Ս rqѽ]l`gbSO3@jg0u1+2oF-`6- :@ZMYSEkhJ3$D4$ -FƟ 7V%7>o%5D5tU$,<@.677fZjvsscadrwucX1;O v}A h&5_ @ $Mʷ7{OM\AhcmW hhf 0 A@O\ͿVf`o;fms`GWW7U,mAػ3ǿlJRL oȤ~SǑ?D%<\ /mH%-o]'aś#ᰴq;HVL۬@nV2fyc1 vXڻm,Ao ;D@nۨĿ:Z21JvN- %"m$h9iK^D4@S{ mJ`SٸJx,Tḽ%no/hezۖL?6/|ocinrupp _ 8IGs `b6 l_`[ #- Pn E_"7(x,JxTFEoFoULm[U/|[_󏀷|CN DlߘX]- 7-غQs{u܁o%^,97O?oj Mm> 8R+Xy\v m?BM\lY: w?^Lo,xƒ|?ruuɼ?w.#nl3#$KQ2_ %l,K~" wRd ҃up_Z(JNh1k*nUӝVp,v'w4/y{~ijͽbgTw,[v`czmm,AN3ݤ_6)u:5sK7zA#֖F;l C6n/BuP'TYȴJ~|lq d64GKmXV(OԠ`Jl)-BנzqEBз力rVb` 4-UsVI[©GH;.HCdi2 setwR|l)'tY-r#yd 髦֏\&@X΅}sAcԡE⋑inwم#(AU=u&ղ4S'5Iń5aaZ^3{j[n(U / Ip74tB)ifRq<*G0q [`zn-),mY* ig~Um]8k_뫮Ut1=VԹDjn* %&5Čfw\ˍƣʙEn[N'+Y=++yHGD3ڡ>L}p]6C\6 ;Q9~Z@`+(gONM8_6._*|5C<ئhwDnU|=oA*-\ GIQ^P6}w+ҎN/Ϥ3I1p='&"@jH gJ:1%sÇ\锥䳱(S¯Y.лsx0_p{+ش^ó]?x8m};~e1p 1%R>ea4A1)U%wvW\c[J#01l!\$z$ 1nfj~ u KFS ̓(($c0P}ǵ =XqFH +h{ yW kk32""#G-3p7} }6xi-QaJ..jܨ<,OVɄ3zZONEv#}y=>Ͼr=-G;3Ev(/(Mh / QOʳ=kgVpzJaV@P%uRidK.wd&w[R;e!c$W6iVDuFwf$eMC#1R8s~ qЂƏjm#^D- '&*y*Kk<}5'1sg/f0}V3D45?տ3lW=3\=j5?4~!ŏ+։[A}}JNd$ɇ 6N1#tP,cEi;¨};voX ;?'zAHH&M?9B/1bYUL2>"ϬGlEwh5< 8r`} __@]O tWV f-Cc7\*^=ʖs/h'T$X1k Vǂއw ;߻X?5j/xor~hp|XJ<:8`~7qj2u!lҬ 8}7t͕t<@k5s+xуٲ\E2~g6}XR7>"v`C!߭<%8cwkm[{֐HNdx*ct*ܠ $9ګE'9 Ahl2 5oو#{Zٮ[E3uj1Z\o&/5`NcXml,D2i@|>0$+*ܰKLeZ==dYw.IthFh yd O_Mj̈́6 Mιsl(#x]Y` B͝-bzAIߖe+zѺ&.Qr7º jLFqty D蓄Z^حM7`@Z qy7s'[%>('N~Ey {<(ݛ|# CҘ EZRM@A,8S}tfk#V2s~]9SPGpClr;9s`% ]!dW१V^~nw ;<ӣIcMYi-2rQNez㨃zOF/]@K(E;iǐ)UN!PW"~ J1W]J $4nPrʘy4 lwduZD00i4%j֥ ݪcP}״Zm>͗i~:ݒ8h#~7#キt ۯ)ƴ8x#x}rf+籂!}mXSrW/'Oc~>e9(1ٮT>DTct-c Nu4ѧkdF)f9ٚwrrWLcܢ^]- BMփg%tߍEx$*eى`6!;!_-1:{D)j;k,rE cDSUC %4R$sVJ P{,9+=$ i 1?;q5]/y,FqQXYYqV;a*iY%bP3mfH;QdZD=k2XSԹ:_x !K|@K^_=BjҪi1g?b:W/•;&0,Ku\6(R,VZHn+\4O'De^j})Bn. hdΤ >=>GZ+P|4w &lAK@BB̨V}j@sdd~dCj;'_ 361^;^L :'U=+3KW{E5;fKXe g_ò_跟X,9Ldc*` g -FQt F]Opd[!2YX2U36)W2[q LQ0r9P&I(,d)+ PV^S8DY# lK7OXfL (g,ωd~ʨ([(< ҙ[쉒AAa()o{T2"ӣCƤHT{_/J+)4ϋUN)Js%k#Bv=cģG[||X0;jF%l*-/>L;8 u4d$| 8sx\CYLzSm]&͔ҍO+溭6*eQ:>;&LaS`HK_  "4m+B+*=-+YԹ \Zi(>x7]辐oYrg@w=5z ҫ6I\܁.-27HKgƲ_ ,#ZR:|yH7?31PVC 11Ej7'sq?h-:3e$M*V7ay1@<]"7齐Iy '.Yjn缹- *96¸v 2MWJ qx,=@}nY7e+ˣ+е[L<4|U1`¶m>d@OZ-jmKΎ$9a  U<\c4 %[Q ХGW lz%DRz#IJߘEcǝ }qz[_.#'$̏(uTފ]&, ܪ$<^frƧg \ֶܖ~ V0 Joq`|@v9s=f+ֈ>_RxpLNgsl,RFq!=uFI|.)Q^v3XU/s~J#Xvq&Gn+DNZ| ߱[ -jEm%"3=K !tlӳ07GC:/Tkacs;Fb[i6ݽ[ dwXzQV/w!_5z%G=4WȰU%$e{ZjDd5+틸l^\_Y6Eh Rϖ_p{Dˠ\j&TV2;ɥi.'bdeDD6c!Iu%nTJ )rӹ"|cO~K7 ãy1Y0 uz!?1xmzznX>]7FUעA1һ+<' ?[|P@V- a(;p@2Y깗ޥJTfT8b$EoWݜro5. 󚫳DMMͰH>`Qw:hf#D+ |ahemWnW7JgރeDU_-|ؘ " Hf{bKdyՍ,rXਲGC ^{Ӓ/^| E{lrq,[Y\P>jX lʼn O߬W݅V8mpd7dԒo38:#HB24,.ЧFWV˦m8ۋsQ1eEIBΨp*`?@gWZ̀P 7!._U՜IaX7$ᴊ'Z]N!JH}ĒouKܛORewȌU+$нQīPny '߳T܏1>ʛ zam4}2Ircyx$12px61?nbѵ"k:WIToVB,6`}{ي#9|~zD9aΰ%> C&SR!E-M|L>dQ)mfwX}aGx韡КdQ٢.ܐ-YH"2y9Ips(\#`a# IL 5"j!N1x`CW|PRfhԨFj|>u ]V ABH0Q Y:%zubqfaR'cxjMEBf;p(ied;kbKױ&wYߍNe ݿz'yz 'HTc | IH)˄淿;|Gņ5e[-'e7|N̳(\2 $Oq)$` g)FNYwa2kPgY#5f!R+|-8?]W1s#x Z%"&%3VHD@u"z_tY u o(o7Q",50\2UKVdZp mHPFW?Wn٘H;dyړ@/>$])yCWk7:Vp=ޘtrL!}r[N<:.s[VJ˕.2(ȝYmz`jlW*>g06!"zŠ^e&!V|Nؤ:'1aEW9޷(,kZ"?[MGdZ%_ӥYe( < t"A)dHfXړf',I `*\Z:nvL͡ڢgԈlm_NNjD#ΔlPFB=p[\@U)Y)L)qV܃\~%STBx5qc;$zlDQi-@Lp^ gŪ A#b+BU84;`Lp/JIGwaZᵒfb&VSpehXi=Kз\X+s.&zl* $t55Ů)is[T\H* WuKh+='K+St`cuBNܜ=b"E`O vܗ~gG 1mٝ[vr7Iٺ&|,~ę/݂t0( B-@|ilBǗHm*0xoײv W*Bf„Ev[IKòsS]rP>+C*fu 7UR°',[F)ZD<<r~4|3n2k+k(ozmzFNo;CBUlAcj(|.kg}3m3H%W@j7/YIkRb(gA%WATn3LӚ4ieMjţmӠ }SO`xAv䌙yuLPYV?Dov_+pt&UV\4ӱ'RDĖxA$H4D^ m/Ip^w~7|աcԕ~ScD ̕r4IIHD=m$*{8Tf:p#/7*`ʿDCe[sH9iTV P{Wn!8c+f7]AG~^1mȜ=ȡ{vӬԥ4M*1 [)?`X!U&/N Qɤ "ܷ]U?`-9^])b ix]\ff慍ٗ i7٭f^i#Do0~O&ݯlir]mlBT>^ލ:=M[Iұ֞ iO0|VS[c =:YihF󹍁}x1 8($&t 陝sN4J~`| ΄o["^:dJ.p*eׇXfqכ[cV碫G51nQjSdfxYI@ed CPa'AYu%Er!mZ NN'I׭}ڡ1~%փ"3xL^<|>"rvqF#}w#F#Ƿᑜo13rKqގ{BȋÏK(j4dxԕ ƾNF+t蛛CcSB&c* N E껄Jǵ)]:+Ǣ]2lyTВDU6k 33[Ԛt_6[kg" Y; C)`ֹpK@ح"6,>ZSr$p٧F2_I}1ұ*˶*!eq*28`;qa%OLѯÌ[qOlj<`KthsQ* T"K) 1 =g 2]&?C٠q|ZU\X]eR@5riQ [XEFX&8}6E9k\6;tO tKь~qEXTO| "$A/O5ؕ➒A [j_矫qaH*':lC^ ZُV g yN">&/uWBԆy qaiUE^[<>_Xa\{G=i>cXlq| ){89i$K%YYj8:E*e3D~g^k針/#:UEO=(ɕn?'^/d6SHlQ&tP ]mMF9KLnk}-/:gڕ*!9Iī6("Eq2Td hqC__!K 408veҴ)EnvS~B'}C5<Ò 4ˤZ 'éw#v2s)WSg0*HK3ï4AK,m@4})KޑJ9eX23񒐿/7͔$'7l~lTV8 EΊc0ϣxnM:9M*S#4շ" 7ׯ1,h v÷T9g$1!8$A r$r8҃w뙷iaTq"-3=f5'Xx.( )(( ]T8iԏl]Beܮ6+2W+<1$>zynS1̮WZl!Fz/-Rk:vPupO~MY?4 Gw\hcIEAIU^3v 0*Zr I ;*5)"-J=Ew/dv)N*J@ZuiKƾ|u &ߵ釦k+#"ՃtivxA8,"ΆvåtZT &P-3Ǹ $a/"I^&Tc E+d*˾_#CYV  J$REd7OG +WEiL-#Iجڮḡ`xIk&dӫckdsgUɠM[>L = j$Y}2W'Kp˜*Nz9E_B8]_BmyHݶ$|I+U֟+J{3f#,5+-iDx^2n]-y5}6 <ڞKd]TjQ)&נx&U/2"}}h[J x [.T]|+WRDz3X髞yX!ĚN|w{rE#Lbf_~NHgџa[n{ y0׿Vd Cٴ(S w(#ZRvhͰFO+h@M+oN7;8 9({P . C3nd;H=3&Iw e7;Clo|>u$FGhcCy|^ģ#HA\O4DyU̜v m{sϡ _>㥀[0ϥ+>!VWl'Mv{>Dwr`x/ʪYWU>&|]rDIy\Q^LoF"!r4M9{ٛ73MM[xrƙ@na.rZhER}Z:.f~sWNэ(/e!K9ToS.$F?hSehʯdwqd?'ksxHFoEe*ɂ}R(!ӝ(þۦ;Js8.Jp:0_.BnO`<~+z>4 \&M1n\(a8']BssONϖb/QOm\yxc*ql7^w &O,dD$Z{!%]L\(QtoO!JS_oHQvf*fR:a~a/S9QC}ilvaX>?*pܰ4 oKr? Jm";TEW?l2K$In&eĞB}Wޘ/wLHҌ|{5:a}>VZʧCv\wN &%:\K(]ķhjU6ŏҧ^ڡEIg|k#Ţ%dϯ˥>9B/1)|;yG(P ja uH֨ڱ}Ò*Ҁ@53Ұ^ْSH'i}w\C$̗j9΅p#yvؔNȍT`*c>6թA#%oģjM*3zAy@O/^ȻΔO]LȢNֹ%=K!A#ru8cz`:MՈP[,4i wrˠyLFH:ɼ+]m)ַ'G"IK^8ĺ+(rw}4pBã >*ݟsɻ>3%R_alsd*"1e˺!\]K. / &ꊔR2DZGk=}iq"r~,o)Pg\ .$*,}?/2ۤ0ڷ~9ӏ XbpcBrpf,JEPͧu eP9E o}fRDvEw(ֺvs$l6KP.CLvZE\Y.uUQ&6|4;b:a)iVw͖|y;LxF(p)6 t]8ʒ{\U|zĂaf2C &69r"wP#R3dӋ~0œ)#T`APIlB뷧")tR ? GICh)寘u7qᘃ=#_mFu>OFi#ak,DD*e%ilb e;6V둼Tw ܡd'O`씡X3)weAjXv9qjXi~z.]Qd D; [bI49#'ԌdWVz]١ w`eU7K6ᝄ+;ZQب=5ELC:~b^nTG[QA I>R\SGH̖r0jQΉ&Ŗc~msPQ`ٮm<6kٮͶͶm۶uo1}3/!z OU=rXOX6\Tl ,w6WwbnxM|?)Zp8  Ofg1⃖p gVnj:N+eոBB8.)m $[\J(0t3,,;}*ވ*|7m~sa6H .v[W5핅*7abaM): ;VQjxާÙJ@"']%"ߥh! nhtYeNsP;)%f Y@A_u"4smfMڇ]Xt_͊[cV&pd3F\ υR H)@K; vw'Fz 띃.0"4nUM<H$u%筢iAjbiQaVl"F ׳E?U-K)}iiy]OK hr9܍q݅w&Jڜ -</* +Hʿ7,TA[+(NWްUc(`0Үhr9hX6RKrxj8+:BEڧKcی<ൎEcK!vrV$'a$&vPl:cI N0TA"U`׵̐G"=D&rG;h}n!#[NA1NҩX%r/G#!bB7iQ!hɐsD }]6 ]O!pNCKZ/LIwji.䳢bXTyG**91ɦ;G+ݹLeFVU J =dto.9kuk,Jn `"GιR"yC께=6L8jشA2;rGq&laxH 5ܱƈ^)ξwJ#Ëy$`;<7{O_tf&/R/f:-@ap7 =Eːϑ5GtG]'cfĊQA eLC`Uн4-jVeSNYwۏ cGTcp?vVpQ䍦3xe*Am)qHBz'@"P[-\6hh&rd^wXU"BC6~{w:SexeO(uHS ( է* Y&ǁX 4 )RxN ut|b+%ћ+Bqf`QUzMf]ZxXICWdYDE &/S?Hoh);bl!jy8){Ulv 1=f:HUy%Sm֠p=?I8߀[?{hyr𪎷43&WCjjP0K9׀e! 3*F_4y]>Hgp,yR#Л-G -_~r/[WCnʿg}d=(!9x`mp*{9dc@nD l{X\ vS6ˏ6@ΨڙInW֠7t0X MD c 2ݼrsw\||‰ߍ]ClD=&gZ4N< fĺ8X> 5Xŝ9C1g$FƔKA~yKp\I,6wR=`Qֈ °D:ٞ:c; ͟9(jx}z4`6=7^dаzE2i{ߔӳiv(ܱQ1ؽ@␨ R*̱( Vz:: :0I:h(֖v/YGZj 7 +݂w͙=<:fD ÕS(%x_i@{ 35.q)aA'Op) H܄(^I@I% r& N.7ܟ$"\Dp BMRXopv$ݵ*)vB9`x:Aj9Eʘ Gb[ 3_ 3RF,CzӪO?3&m|S-K< }&YXn+/{js=QWZmɿOE3V=eq(G Ⱦ_lEڦ~Q8 n =4RZZ9pP Hdc Ƴ)菌\''~^?C$3PC] 0qH@J_LE[nw5У̓pWß62pQ.W oWʥxwb'Ho34NI앋4[AQ i+xֹ~\qIgOHdw 6c,a[GTKq'c4%Y~FCpӌv5b'+-0AŃ$_@5!dRnPyRn3¦?8Hхå8̋󠵦/z'[qb#мK_ÿyZr*-!Bw̦n?d{&׿ -+ÏIXW3pp&=UL9&sJh23Ww|< o ^KYsoz6jK>K(׆ZF )7|Hdž&Ŝ(22#,/] \?1> q"U1m0$ʮd>a$vp#7뺛ϟLg+ x>gy&D&l $% (Sj,\oW3#F&g]M-h=jcY(-%P^g6G]]g(U|*湤r0-} ̳=c ;88J\zh+\ V,)~,b^4/ڳB/vRǁO OPw, IIp]zzBNZ …[id<1e,-ɒçT0p8O 0μ:∨!̓bꈊP ne)XͲ!:=FL ?T=$y]?yu?LgJ  3V)L^LZPw0ٜ^١-Tdn7m=Ok5UG/? Q.$7H AhV0oNp^5Apl=pLb{<O_qkIܤF/R?Uߒ~վS\vޖF}܃OHsok 9($YJTb24|~c sd)of`әh"KPDrY/wjPmiHTdi.ʹWKIӃk!%!VWqhX RSi[iU3|nvJ%nl.5$f|U#Ѩˎz@7Wb 8k4̦F0CMyd"mb׍/1ݕ|˷`X G(~YNtp7~Fhn603$.O!xJJ‘8le&z-MvȄ1X0&,fퟖ1ϟ0y{BU7L]|*ߖKZF;%K5z#7z>mo2*#,"B5lC?>0|G0稦 i:fGJPȜAV S,A7ZP֦. ھI|q*s) DrNG5jۆ*ϱG@tOM=/L&NBk%!>$hVJ-@-EwqF{ċAN{!\k~E',ed/iSӠߛ'<\ʷhedџv[/E7D|Wb **әJTx7l4jD1Ñ(,Ə>TU'ش5md5C2~+(ᩡbݻ s~sRwPr.әIOqm.k7c[ 5 u' q{U/4O4PwGPMI4ctNnp~rC%7<[">ƸݒVfF45Ā s󵬰*4_8]؜;-1,iqqCrI ͳCrhp-`l\J D犣#GXdA6woGUe>QW) Tػ?*:o?"_a'8nfƂ t Ƥ# ^)ٜ&Nkhe>-6-ϋr{(EEDv@`{;.A g\nG]-лU :m0:lYIz'o}* RXPGq\$c;c';x[e)R <Aʖy.6XߐdߩBk:o/;+_Dk1w%^ZJP"Џ@g$&~xEU]HzPկZ\`H7ٰyscڏne]#Ƙ$;kmQldN\/{`uW0iOmI>6Db L-i#9Oygv} s<`X=GC =ۑzrL°}؜5UC@Y., ()5 !\鿘v'i;/p;*yh /ƺ {8D{ИoâWR.|L!%~P,QVda{n܊Փ7|tLӪ_栨V+`c( ðD l "x縟l~d(0wei'HUy Yv2LVhul`U(_5iFX"%{rUt[7OQW 5i!Mcn$0Z(݌oTu="x_Ene["xM st2`G]/WD♖Ѳr"+߀`L(jJZxaGkp{@NA{8fN!+J,y[FVJi`!z;\><6ᡊ&{ LR R7$}jIV^A=|ю(^kCs*273-5 -6INGԫ` J7\|XgR̸ϖMle?}Wz!ޔ+əm /ߋO$wӂX6""$yǒj8<?[9V ]\v }7e*e(xoJ?D+ C* S jJTԌk nqḯ9^[U54Ar{^~ ov=f2-c}U$ #X #pK~7?l,+v\X څYPo-=:X^a;"\CL,aZ.[x0tA~U+XxO2Kp>:F>گ9ңN_ endstream endobj 164 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 166 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 168 0 obj << /Length1 2906 /Length2 30153 /Length3 0 /Length 31811 /Filter /FlateDecode >> stream xڴuTM= Np4w!x4xpGڀN\@ ,o& RbtO&27YX98NNƞ>`x[ OA.!W_06 qD . `xLA\&?$ 6`\>A`.r(q(A*AA<: +8 ضf? l%2q;hW8qhNfjo ŋL? wC?L*jl' lOp_i]. S}OKc` Y% 3 N0?XݿfS ޳Dڙ~ZE,baO\|߫tO(?6+?k-ҀUutu&ŀIݟ /9V9;[LQ\,x. pIMnAmCpR _,L^@1Ǯ[?*c`U'{*W1xz2OCW?_EE=R1bc;9}k.F[ "~7 NV'Q<[[PK6f5kW` (TMUb/'ͫ*Ӣ }lOa,g'G"!2-PMFs$_]>ݑA P8:~FWձQƽt- gϐ..aO:Хizg]U/Fn# 穜dUFcA:EYdɭ|4f"QMiZSnQ!2J#oq)XLϚԎ/؆Mo?|]N'П=k_9'7ڧ]$?Np؅kE@,<|z9~4GiQ!/IJ'y?S`S'q'2>qrT)b^*IfcAޟ~S֜`mY Zd'+*bN"(СAR%N/ǯ/5d^;],#Zxv%Z /e6ynpͳJl1 eB%:ffb[?Y0 *4DP`ZJֵǪmF$Vή|<+ ?۔|f)پƬ['ԎoLb[5(-;3"T0YL)k:2imZbow>t%,ou8 ,F? w%F)Z}b.'8UHŒ2:/e=} UNlZ)ᰐ(FXPN{|BQj~8@mO 4K_k^& qP2n?!t;*~)3`+[$}5ߤcZEV 5w>[xFzhހ,\ƭRjݓ4C5ey9\l%|/C`RGu B _8yu5pq4s'oOgn*#hl#ix`B2ԥ5ԊGhPI7<,?T\-* g[ O㫀,a=lK]Oqzr3ۆe<{<\F$TOG6+jSŒ҄Z*g˥H+i9gד7}k0WA, v3*lUAj^xX?,E X(<ѻ+]Gyϊ`,0E)e(9urHWISvUݸ˔?$ntY9ύJ';;P n9 ,؍y`g;  ΄iQ%|7gc QomT'UA\f|=?| ,uGJk6e>K2t8L @@?w+ʓ^%||ufo@⨮)C Wx )Zz5K/'vղ&(HpCX!d2#Y!>G/ p1])(HDN] +4QzfplS˵ ,FB/QZkkD8'PdbW%o]3V'Mo;ovCUc&'(Kwl8hXiIqr ,]gqnE-: MMӗ='g١\wD(ۥ鼎kE3`G3K2N߬SJ-]inFe=y4&>Hi#yyk`pc/.FVXy,/1ja\)+Aj+`~~yI>ueu+t}"+Jjv$_MvήfM`3$ٗLo2KJEΔ8x/65>yV\I˲6F/'YC"+ts) I£+*PHH#h0cioexI/wcB٠BZe,A&װ현Dċ9v|~d_5}N(ZDkc w_`>Bu ,3WZ^.HX(K:ؗ";13@2kIzf򬳫X{(CWǙ Dٜ+NA׷6,UNDZC˂ @3شVE=zJf r^/lao?ls#rr3mBM +Vr˱/0IȔSѾPͩOIO ߡ'.t6~ veY([B%å+Cou4d|;گ󪨔9T56;HK(A#7oT*>AHw-?uo3I:YIVc&yD 7Dտ2?A=n-,P{JfA k"krYxL26mush@ç:>뭱;qa/75o5&_w%"rҙ<:Byop\HV h`0["Zsg7X4W3QEAUCL{ 5MeUWSX|a/P:V8,Ld ;X$l}S9w]ƌl/LSRBq h!+}J߼ m`c>Ø4;u#z]s.V!汝Kw eKrP@t#zEOaV&d _/d1|6- _MVz&k'?4w>ھİx9of~cS%dJ1/; AEcfyk% .'&[5XV1as1z`ye5l¾W;2fFK P(ą(0HVEQ-aHby!twUF}`  o1P|=(l:Ɩ^`kXf, k+yxebԖ-nAA>]cM6;πlw.oE,ځyڤ8ai=T5D1B3N̙Jsn3UDXZYv*"ʆGJ?:zqDL8d˲p,rHOKGG }ى@Kp+]3|f)48?cB:3ms| cp{L}axD@ ^mA38R!=6ClUpV5/@P8HԟYBp5Ovv0O1RX a/D m_サXQ֫+YVs6ْc[O]|;N6<#Urf(6~ż h"=6P9#úOaϮ>.i<߉1jNA '&1G)̎b;*G ,&;a$*w4P{s&S3b0PvcXTp2cR*¿6Q5"夵SrWt%p=9|!MyKIυT/a1=U{/Nš ]ҶߨYt}Rme}NqYLM$_!?>Aj8Ic}̒:8@kb. d3ZHZךp€;/XfS|ls< l֪b'jᒽ15(>PT Z%q{j9,QRJ-uڮ䃯R5ǭO_.I2P#~qs17%=vxkUH#9mbq-'5vn2!av;y0 |GQ FҮA &B Q|/jb{'l: wY]˹v[Tm+6H48$YlD&I5xQBm H6v7W/P*6^\_98i! 2W2YĻsnKvl$Y}bEͱq9muA?HoѓqiPJ ~ zkKЋe;oV_g-}%WP V*))dW6Ruwg7511YM-^0XTh[X[!yCR'/nA^MȔ).ZA_K%Jdl4d|{{ V䶼aQ-F?[|(籆ñ^/y$KCg,l${첆JkBS""Z\: BNXq>)c/]'N24sϤ|2 # )y6ōv텴ՏBXuD87E?ץ@k۫H\BUG!" 7d~i{Yyg 6#׌Y3Ӄ^/2i)*㝭ԭqc5O W*zPܫ>'J}xЍ_ض}tuYB W{b yPTv{Timu7?nt"gt`!aAHj&S>2DH+xӡ0殇fKmʚ ~0}ꞗ;V~z[{ײҮQҍ#&ֿڥO!K` ]SFbM6E݅֨OVn`Z25UPiO9oc8fPq"%ɝS[1N7GaFm 2>ur.S序&LXoC^IƃRؾqg1_Hm%N>Ij#n|m~AlVaC@}3n m+XkXhEݱwHH72c.-ٰt,hP U&>6]O/ML!67Rξ ޛɘ~.]`!iVM}%@"i-`,T*/hDy߳~5m8,(S; u6%5MVj;D$:P$ $q[r0šfaRէ] ذ|/ =KVX9&MEhD%'[0\CݑsF蘤osP-j I ̎HP y\Fܔ]j@ĪbMm0b,ȦX0CXᄛQdgњ_耹E_q *J[tI@ڃI=L%#jL~`[ ;u2dGm?QxH3`%^^6dpϫ8<l(̊ަ5ݐ1}P,MeH1]bO?vWFI3?"@+庙yaV}>K0\mvJbth4.G7I\r3閺Y?𖈋 e)ı͢~rBJ(t6"#h~2o8!ҘէL\4ME:h\фϴ*k,kyR<$@tэޫ+`oTen탎XLw-O%sF l[$ =kC;^g'IR΁=OJjErCM[=W^-GHC>8#QlyQCX H×VYHW Hh^*&4nڬa1<})XEu6SF+vh4DafYfbwW ZZ MYN0%n-stGzRqLe$@ro4zgS|2߾JNԗVBSŜXDB4JJGUtn:dYQE21{c%,xV?(iOI7CXJD{^9ҪW2mw:}FL %ځR0YKXA-JPC̒떋z{}mvOD8װiUj0@] {ou>ם}tDFS/B1.:k]) n hn_VnJ7ɹU]r[%TZ= oFI2>fmXZ*c^YC wި,Zs'/)V/!|7Z!MS :NNYpe48nsvTml᭤'?me+=Ci'&E7M 3[ʦN6WkSh'w;, 3_~s~sο oj,6w_Udg`e08i%>}ZXFAXVASbOO'CW (.(Twbt W4|tY@]^ ڡ%NT29Xy[`& 4V~.FG2*Iij wxhq!F?#Eۭ?#dě]'8AnZ)^NiO1jB^-{WYݏIM c7㷮V% V=7r[EB dYpD!q8",I`6lڬur: ~&sʬwO P( _SF?Ye K&-_ɫY[H YG팻hEw].k}@aJ*_3C 97PDUXvfzS $MۨfB]n[?}e%Fʤ}%\iYӈolLs.Dk fC? RtsWM8bNTRPY/ௐ,_ղugj%+8ޡ8l4a tZODDT9=Bk((7((;ո5Sۿu<dRu[зr6 gL̓+w;/Z~ *q~cf͙q9"͑L;z-I,:*J=\l QIfxB=ڜJPR՞k4;PI>)!U.\FYVNtǏ/p ,DǑgN( Tps%Y,h3  zUţ֌:'ï7B3Ć'AqHI_,|KzrJ:䈄}g8L"КI$SuܡҝIxJZ4KqJHhޑ 3nraQ2=~d>G:L',~b!@j@PnDnPHg**n ՎOgv$9*|ubt$;\fao,|yFU>#&2b& Yݡ`B _"7Rp#V.x֝p73 -6xpqJ۾.0>JXl{rs=cv>]s6N:NaKA?|]$fGءZr>y[mK?wTTb/[ j1 ai;Vkt{ݚ72Ed~ӯ~r| >Ps~Icbl'3v'eHSkp"hTH<0u 2|rQ:J%3ZlDX=mo!_"M r]:JqHEuņH-hŚ8F3iJ74dD-ȎRJ#|93x?>\93>B/MgbUIgI$JwO)**7".QޮrL"[8J?Z3]g.RLѤ@aBO}W=ao("V`>u#+0CV,M(jg}yfѢIG= uMIX[S i[ 0_2Zwh2emdrhÓ>[*_씅 Bxj߂KkI*y 4TWqCJ>8b2~\IfBPAdŎ%T^|'GHd\$yFV~n)~FOKT/ /Vhr9-6FuDgҒۜq]sUcTOt=@KIϲK{,c&30Q?bf ayqE2k#L^Ny7Iֲ|xTHz1Bc )Γ!/R~wK i3FQ2zxl6j'jGwqO,cX-9 &LFX=qc;BK;kP9Y'' A3Munăf7ϳ=տ|e'j6ɐ.6Ǹf kT| [Za[`5g2<5fhΌ{ tW_*!D8YiMʂ,XFa=i `#%Ch?IR3ZvyҀT[{띰b.c.)ɧMcKN2:Aœ mTaKՕ/")ly!ڧOm`ܪv6Of}0n ͼ_,N2THGV"*]-6AbiŘ=e6Aԣ*N-.;t̼sӟ^IV`G=+_"LJ%Ҟ\ey&l4?4xﳜ l|d;Mhv\[P61~7:TÜDa]ԃKȊnO H٭7_r$2'i 6ZAW1/bMXax2MnuL$RGgLGG-w\i+w=?98ʕCH@JBe tz9 ϗ[Sd̂3k0_`ކ q#^[Pd(2)ϓxk .*b#*=o(:O>G jb9g2ELjb=s>WDReijeW\˅7A&F]"2JНd;A1859[m8lZ)h/a$P UT 7-NׂTS{'- :٤$\&y}68CZP_Vor 'A,<66Al@oP'k>1_ef*c=NwI~)eB&2ꭖlI;}4Fw;TRw>//qrz"_ \jP7mwhrW6rmaˡہ{oߥ}%ʻ5&lF_ga-D$2Nl|eMa@fJV/]T+ZୁeDWMZcWzbh­_zʪ7<^횿ް aEhw(.o5F* Aݯ@R Ze˼daW8߽A`aK/64۫soh>hB\/YˏearEvD:~jxʊ~mP[iٱIf¼=R^~YaCq]\zTN1)i;ql 䩞 7MyHOXC'4`J.A'S\RќjhC۫t<j"?3RF6ﵷpAdy5 E)ùZ˥DQpu= C%ȥ[,^#fL;6[Ii;&IvX,d)ܳj]CWE| 9fK쭙4(zxu _-fD>Ex ~HF㬴oV+9`wH}Z3@UEAwa,ęX${"`i*}K١& $tb³SQjCa;,H =y~|pUby9" ̒ff #i`7 :5`Gm"uX0˦b^~$g^c?~3!K<Ŷ)5$*њZQN&sJ'%Wwe!8M!(DyE҂3NQP]+9 >5PYns Ҝ*v5@PukvhuT+ՇFzXKrNu;NAMZ&暧#TitNV؄u,ʈ- }R+}[:>MVMxg\A@]u˱+FX'u X2I颩)Gb f/{ND_iGuC mlS|7a_k=}XԮu5MQCa }\& Rtg{?BI}|8#lL`=_FCn%hjiNHdZgr\n&9[un |g VVEN‰t4h+%D(I) Ō8M{h$xU<w)pvؑ_A݉Acef6WIav>l^)SÌg  훿`@5m&a0%Se<0`fUH Wrz'3%3gR(qN cL]_TA[̾o;Y4k~[dzh$;;>e~^mDNg_1).Lpc#ۋugC>2N/#>i(1)8p,wՀʅLfrRYckbH, /~Sx/br*'WFPlV-o]҉s}~*o>?!tj&td! ̅MjE.f,8$:Fy6䮡rX?Nc %?h`@tɒ#]S3~]uxi)7DFuYnxAWx;5V H~D,[ƬQ%  >2Id9(dƐfZb@ϤZFEo|xF4Kn)֋( aF < s> UUڣ4'r4p &ӿ [  ż̂+aA;KҴ!t2+m?.ǪJ(SnypSb[lqjZˠruݱ0O3SRӃdxZ6q1hō},k&BU%7u9"D9Q,L %d{ gWjsi.(I9ٓI3QoIy>@ChbZMNFR(|?_xr$1wC,O!M$ tl5{d FϿ&o~dXlbD 6:ŏS3Ff& a;Ԃ(^+NT(l.ySK[[[u sXxbyѥg<܄on8"C^1tXVV4.wo V7T>CZ >Xݛ3TaJ}G̭Ð b&w(x465%>#c "ePR+b)BLTӿY`'$du~D4N4ĎPTvCp`0b?&8AK*y _.4ƲCUƃF@ [9y̎LQ+ >d@'}D CuwkwƄ >)8ɷTK#[B75W5>K׉rinkv=}HFEּ:D.`{ h&;[:ƅZ&L܏z|7'5+rdɥB4W*-RY=`- yA1;Pmws-ٴGL­ tYlbUAda9Cņ[q ˡDUDD ɩDFNR9#.Cjkƴ#@YKU }fNCgV ^GLy[ͤU?n;B{l]=RVTt-1Arz" GO Qc[YU]2h(`gd\ ROGSN8N b3\F#8DO1EyMgqboaֺF}HgX` Jq g,"! fT"x$CAS9;l<&ٻiJL*zt I8 7!$BSm]X,`Щ|LjʳkYQx ߋ<A3,bkh-s$sZV*}׈A"5TʣbyAbLα>^ 07*u|R$PDz94/uDFjtV6*#тy"[v4gYKZW zKcyj|& j-4k x"X1 /EXx]Z"Y|`{7kWOˊ#Y3dfu t(G2')7|0*m| g}2dڠLM/K#^YoF^bAjx\T-W u_L="JeH%/ ;//qJEҴVO_[&{|^crUzWXBY7=9(F`k)=ӷ ,_8<`^{ŃXdȂ:mŸate``$$[)sNd<#UM)O=Xvy/|M` U eD,ET]1خqSa{:w憖?k{+4FNy#ܝ.8[o*9|`#5q^@J>< UoU ~GƙlhwN񉲄^xH(mH[e㺋#JJ"ma'$3deEEzt195OJJOG,NZHU?U8x!/}Z0$RHFaݼ%qUxHK  \1p4XptPdU Xf5e7`W❺n*~mnOd3D_`L.pVX?; VÏcLŚ,יy 3yyvqgrPM2vuVzS̪+IԠ0KU߯m0I.ojI1,,I|1_ b(\Rx((Z3{"Y>G+WQ ZK%#|Aw>ꕠ{d`*,Y Nx{}תcZ04$;D6bh'ܒQ71H=y++w>r8("D̚?IͿJ+N &g&zk".?A\<3)i*S; teŁw"h QОyQ'djO5/X) ='BouQɞA=:"Zа*nvFKMˁ/3y®U>B XZۖ=(d_gpA b8, ia/,C@瓠;Px0exkÌ.0AC{zQr>2kngǼ.ihOқ)I6L/t@yL 2f_^=WҰ#qI/1|kP"\tr`,^]^iS'] UL;ndrjAs0 *]~EJ{Mb5pᱵ.l6xIo/(;p# "~ uK|⭑s\ha/ q՚s#.lJP?m9P|gW맽Z.`WGes<K6_W[wRЋ^+j +S ^-X0S6|zf}$!cX x"" 8!~0L[!UlouL;-8tޞ?ch7G% ."Hϩ|klD/ 9]&n 7Λcw 5T"&vL;t<|婷?SV֢9#co:=9F!|(nkQ~GNY9raZ(}CzKфHVy ͶZt+ n29ڭl7Fb fe bgJgb^TR#[LF7є)^DS!P6-6#koB+r# 7E{cha_6cKoG(ݭ8ZZh8S{}sG_ h+`{F2G `]΋s%i#BAJTΥM 5 \OxВi0i ̶ &,"l: 1OqW@ԐUl\#!mp{Ӧ'|-2@|*Dxnx9Q+Pw<0 ÚbQ֜ LN-P݅Y^Ƨ 6O] a J] E"hu;&\0G~h31oo%\DE9\H۱=vjjM_f;lfp2rоbP_xv[&O3y0A>Pat)\n"K2G'_cZfSjkN_@(r8])y%Q;KZ9!q8ǯB!S<\=1gc$8'|0Faj(02*:_@DMi_ԼVz`3zJe3ZE7ѣ˳r/ W=vjGJFژ*^v12!}٢BkO w ITP~ya]&RL פ^ZXZ*K>8|D/"sCXྜgO^u[ps}vz{).D[#xq27M|OƉ_F Y!ޱ.Z&V0fY2/@"D?G( ".,yɉʩD(PuC ArjaMUEL誏js'&ˁ#.Y siTfp %yϷrP;HKAl$^Yai(b(qwsy BiT0|ڍ?NZ}@jb|8uՠNy`2>PƉ:88Iܘ )ZxZ8 rg ߰VFW*w8P8rSl B5:|ät:.bj4?:iL`v o5wzޓjj' $I8`_ ~Epxv=`ԈJ]3[od,,f[FJ`4O-6AWK䙺 Bt^@@6sH7+~Ԥ Wh87u/4xOFx`"pe`n1K s'4xyQ<ȣxݓuf2[>Z[)(6!]@K{_!"rEcVk Zc/(6_:ԅ~c &*眼&LlpI>M]/ܴ'.fYV&ظBTC$jI9݀?0pz@++C IYiv4X+Lhƚ~xUӏ0PE|ZʇKƒ5Ͼl] b=_80l''ġ;J37_4{*ku\/!5Yxfӫu KG60L7t7hjbNaR ]}iV (BΓg8M}Z>Wʻ؜+0& 9~%M}=XkyT@z+3>^@$LKcZO[7rT䃸ɨ JZͳ6o[A=LaZ!@/ 3enu:D4U6.O6 Jq.d(ϼx=U>/噞:oH8,@ԃB ;]6qTAO&&?"n6.5Gn&,q]8 K|~z4ٓ€\D7n|y ~G~^GDvQ9h-JaZM˝&P0)廄cq&W,a- fŕ}9N)91:i}T8JF(c G@NU h {~c#d$E9Nc0)jRo=e56 ؜ިa-6Z)7 di9*2b͚1+ y;V7GNlz@0,ӆU3H"tw}cq0]id#PI><?>0tgk S.oMfiS:Dn"28EÛE6 fMNCJv⩥sP dd0YJq@93a_hT)NAkvN&rVmS̚)U?zx[ %[ZJdo MgݟӊF{K>viHD4}$2؛r|h}OH]j*"28̑b15}]u{+G~Oe/ͽt‹2(g vREd7m5ƏԹu¿pj^XMfOA #_!#~&tpN`Lד˧:D421ގctʂ] dh@ 1U#zn  ?Yr}02 i=g"a2VG|hQ;*D.=ဠbX 'Ghv1NP<^oҹ[c-mSȬߠ!>nR^Aٷ!"1EQmg+h:CN>ғ,<<'I9JQǹl ݴJQX$N'Rw[WA;u2`@G9%dրOӡ=-(@:պ14ЊcHc7`Gh.!H.?c:.DPbyj.{Dˆ!Pc7 Sߗ>bWQcp@5_<4,f+]ݤ[aR6omϏ8;ul.;[RՀH2LHƀuQӊ 8ܗ^gtT%ff)0ȷnx z,Lfp}^T /ʈ+6bi6 : 'U{!*¾ݺsjjH8Bs?R=FykBSdِPNeE-{d.He"ka&Tb|!N(8\ \X"du/Mj~>gh K;ѐ˫vMݰ^hC3n zyV广BAWFc4?q2d9X'xl ӭ9$?s=@sJ!Q =dDI2짚> 1&e$ n\bovUnc.Ssޙk%^o+͑&!j=ҽ4M=$E@xʡ#ӥ) J1k7kԩȫEPU]gj b~v֊ٳpٙ<nj:mfhN 6 u%|8 g{vr#LTƺ53?ģz\YWBw,ѿT]*p9Ծ-D*#LGT 9_f4I .Nx%ʥ$ȳtjAK-ۀS#Y4zwøZasbbUDA+"̓9GO"AG.4](|;Lmϟ=OlX9=,]Lz1ϛ8dw0ߊ x\vҡ6&~k5K#^9ZwMXtz%^yIWqzD7!Ɵ Nwh֋'!0|adHL{ɤRT}j9ܘ̬_DFaݬtG~+E|AGuՀ1CVܸeY).|[ϗÆ.ih <'b+qM 5~ 9^DX[V軕e[f٘ˉ[*(` bcs4SiUX ʒ!TNBuX4>C*td]TB8.lS$lr3< Y?f.DR#׼SR3iY.JU(D8ڕIZJt؊L$9k}lZsw3FkTo@k@z4BXϩ6b,t륄0w9:L]K@윖`Aeg$k3? ax ?s{t3ӋUU'Bmù%SuZl!(8[vS\sJmU_{$49wgt)Ȓfe>|Mue7 FP%ֶؠfɄZJ[^*^~Vf2׻Qee]~x0<!*аJ k]ږOx壢-5+[UCY_ u)ZS=QPv*(t_=Kg%b=Cm$h峴R +E8h% G #c7fbk@<{}*-ll?6.73 Yl]aAz ;(B3KZ\pKE"ӫwa2Ǹw'ӦH T%~R4%&9 TGD~φ.hpP3i\io:V! se|NG9/Lڣ%\O%"![܉J:o˧P>7>\YbYO.$J/QH\3 \#u+~d[y~T@^kV;V14u;j7k0stL;BsWm2,n 'AAkq,_#HP*/#!ۂ{eh%$\BPYYƓ\bŏ  \n;uPIٓA/,4^v@|“$!0s5S7#EQ)+D^C0REݾTr R[h3$Jtb+m`J##p[.ՍJ)[,V&EsDd>ѣXnYr[|Q6R0jl{}fI-)oy]3`,k㚬;G.['CZ:Z`7e>T+.{p'R+|82׮#n1 S 7*Ue5n$(1y,(bJ#U=9퓏>*HK 8셛#aQd[.= &Wy,m 8#VH̓WU4Adf?Obp$X-qNmCσ=-WLAi1]PZzYuβzĎ$l}v:6iJ4.QeIkmPXl 9=Xm`l ~؏Ȗ]*E\Ϳ%fGvߎ$\V ] ZYRۍ&j-mBrѺHptC@P-n Zn+ NRBUQ98` Bz !-"䄾9VL^Uf[;b s;(tg|WzE'Pq -(kCnrpfju\ %<-;vA#<=LmғKE)H}̣jN8I 3tl[sQY)q iћ&gCo=۪e#RqfPNRۥpF_+^VG.j{^v :2ӌ3K(&]+Q\;LkB3ѻAsħcU/b N PՐ뢏 އ_*`Oap"`!LEHm5C'BoIȧ7Ր=\vqmtHu scM`( RqjrD&iaSLbxSJzż3*"8E&1C_uljnU=`Րz/!K*~A.3ͶhHw]($#(9&W񩮠 #P'>ݩ$ǁgdp@{N# sH=#1?𿶟Aˆ^Z#|SӛM$&UyF7MZ;-bQ7υ:X)>#5? v}Wdw(#FG jw/mPj{(L 7M̉-cTzX|;T-OQOJK~3BQ޴\ {U#C|SLbj7䪿B&Fv(g KFW-͇Ud f4lf%ɲIj7If>jџ5H(+mʅz:mH0δRS&+JJLO7lxPZ4 t U (s̔8єSK%lϽT sLQפtr/R]xZ)83ERDǤ?Da? '(5+T/Aڶw^ޙplT={[5]S*6-oSһlؿba݂վv@;Fu`(.M%+>wF GR]N9gb \߬yR D73Cݱ*`E17W5{ Hk?㾙? R3ϮWTx{~N4CH ]l}D&Ñ¢/ djwS[L_m f?f=ǣCuqF"a6u7Dq3ˣ6IiJ$\L(힒olM-) ruCPz6'>0٦1E [r>X:x8Xt<'Y­6Q[ M 8&nF[B7:'kT&u!Tyv>ie҇cwd=fd^PQK鍤5IG\ @l0 s Qc&Q~{!MbJcBa,bb5ː\ݶ!@kɈ/Q6/ e0EU^EXB`\L5Zpi/O`fF"ײ D6'ҝN؄: :B쏔K7ɔ"We/ 76SĐ8h_,0)ӈg'YAklYXHF/, 3]n1T-#>>[3h3{8nk_" @Kʃ46!2k ~1ًg!{1#7-"!<*.>L (Q(mDj$ "׹0b ve6!ǝ4cþ[Ld!CW5*'FuPv endstream endobj 170 0 obj << /Length1 2482 /Length2 18668 /Length3 0 /Length 20137 /Filter /FlateDecode >> stream xڴeT N] ;=Cpw̝łf:u%2 (9202dde 0 31Rhh:y\ycWO{+33"%@ڿ*MF@GC϶@_@`d,@@Wag{ 3s1~G ehlvqLR9˫@ ֦)@PUUR+ɫ*(0Vv_.*!9QP =@NU𷻬( ,go53^]M6my\\\͜fbnp[^?2ym9 ca 9;VڼU^; h QPZ C񫡣/7Єa'{Q?Vci3997[1`wD ;{gfK&+$')& :x Ykw@Y'$":\VVV됊L66Y; nk=V %6-* ()Ư"?23#]͙~5+,ůMtL DwCg O"D.똿^ĿKLůG@5y&`g I:ܲq9Y[XXYJ-1Ah`hlW$2X8Y֨Q֯~,~oW='׿tSil:88yR_{_w&%-_V c 07: wש65-&F` 0#>QNo߈ $qRA<&".f`XLIb0IA&?AAzT^cAA jE<AAE:cG[bcǟ5E.7s2k(^0pC*1|e4Zg[s 2b-_k'k6 kBF9f``y'`?2fy5:}_k@{ ?:O8WGs{?{=m?c` k_KtY_Kr|eZ۟^#ne_w/h[YCG{ WmE*oC@O`Wwvk)_f,냊DsK`(yhHEYHo!$ 6Hz 4(W.M?nzz e1IehO~:/~:q@S}9%<:A+x+VdaANΐmg;]bơn*o61Gaay t-Q_sEV3fgN磻sqk[{% J܆wR6D"WN| 7 <︦wҽCݗE џD/#Z ]:[DzI*^V;Qk0a}!!lx@4)}qhb,Iҿ)j؂[-TBfoه+ͬI{`ל;ٕϫr-+;: 7`Ъ#9 㤄d{K49Ha#Wr 5E} Y܎g:+eDED|iD1ӖA-ob uum4+.!7&۵v])Lr9HSZd{rNVD(* 9 Qxw -dܤN, 5PP +KX*oLY5h5g AE)ON]BRjH&f~DJ I^NQʑ:]8@'/f>f)"8I>>wji'@{J+#}vcYđ 4_׍5֛g| ޗYR5G0n5綥ԐNаI_:㚏|[tn>HNiͪh+JsԉU Reu`& !ǙJχp5dF>G%q&F?d~_0&:}L*')ivv-BkY?]s fU3Ik񟵃&# ;Qb?#0Xu"::X̀-`iWՓuiI0k%gyY?~ $J'wgqؠf"Gy-C%&T"ƣHgzU%3(Ud2P}]V=:鑾և+3 3tUW;8Sjb? ,%E~ 0euRf;FvkS,o~=ѷE T<5\L.t5bXNlӭ̟n辍\5eaC fw_+'btɑ%z@,rTyr_WbRK`J5%y g³ɖM3⠥?_ fn cCΰfcAj٠ʃV}9BO<5?-K\D&' R No* /XWۙM(J"?e{9od'3T= |v G465zNuW)nN}Gy]CfzY=.e\% )`J[;c4],Y<ċ{, y~ TqoR.r7o,Jn&eȁ"DFx(:w-)0le^RGH;'.ZiaU8ƧVqp&f+M̻oekYڄ'a(µWIBO VMx5XZW]<'bpu z|P*Vd2߉ߣ 0&9Qj&>ae[u:>`'oV)(G2jm1nq6o=LOXg1pH@cjնXh,` UlF|$$ϻVSjO4,2PxH>X(.ޭ^\,zˠtGjUa L9,ϯ ,BT.ϯ$6E['J:j~JF͋@i,}$i gKL،,v'UFiԸcm[cPvot[qam_u.1%~Hpӏm~KaO+U.Tc{͸=`x̪ ;ZQ|a@8G\^Ќ(auRArwĵLJ2`5$vک!͍_Vu"R4nR.Zf:@BMD9@O; 1'Q$ֱ̮H'1X{ 3zWʚTGqٻT( q_;֚Acu,SrqC@o&o|*3]ґ<{g9| ZRv 1G޵PK_^z{ߗv<>syh U \l6n%p|$)<5pz+Ju 4Z)yfԲsy`OC$6$xDQwGG\}tw/6m9 &;6C1gq2p⣨ľ5y0u'sV~PcR۟%t GhM\r Y>j!$ĺXGF引hSEfTz0% %q7YI iԇS(86ƍxK ɷLW8rkU4&.T\!n1ٙ~^WwdzăECik(i:^g4H|VNz[Ģ9*+D\/FǷ@^ !̩j:ki6.sqK}w3-R6)_KG*ngl*-_!zi/m87Nm9ߖF gVb]Kᗔ81qP '}Bϊk18\u𥙼f̟.ۋpsˢPILD#)⨠A_4J M T]YKϚ_!j}2H SJ.be"+X%#,6o ѝO!t4㷹]s HU/v5PhXZ%*未tÖ+c6Qpowz7/:{8*Ь6Y-&ȉ&a5DS>W64o a<.*<yfk!ILhҞ+6kɏo@$3%\k[\R9ʵQ'!jVş_fkFN_2Eb4rpB”L.i c[$OAo2hh*[S /|3$*ѭ%샜ݏ)lmQfFtuY/Ka|DES]eVMfM'bQa88kUL[7'DՋWMMH"bƿI#iw/Àq4Z e"&ZM0%WV~N?>ic'"?q-MP͗gE6 M1!Y2USZ`ډsn4وl+GzL$gQqDނQ#t~xYȬdIR_#鏲ޕJM:Pq_М\FU,>c^fXe`dWzb|#e">r>\S /+]#vWqRsH<$u%U~zF Y"b1Y;|] 'IryJ { ~U=g NKv GΑKTc䝵vV[qLzA] (V% >{bPEm-q >IJBap!-M!ia5ȅH#vd$hQDALT8Wׁzj!L׷tӓ7>w_<ZUbȷ@EG="3A0ǁyS3N~Śj=-L2s2c$d_/||ibq()$]&nOenzUdhcy4|@oEE3R}d*]؍6%_XsϗgI ,u T~eigG)|)U=՟8vqxn꽽V0LݶnQ W8K HIM lzlzžyo'\ q /ɳg¶JYZ9<ɄWJ?\0CIU=8!Uum䩯:xzϙ +]C}ސG$aĀg1Rŏ"&s^pwn"ij B|ʙaְ1@Nׅ(鸨}I\UF/uFYa;Go)~1TĎ]B@+? !܉X90خzSGEV?QOIQ;BD$ao`m.N}S_#ɻ}uvt)-!p N _\2VV֌8f$L9 kfax믯%(~^".*~h1)K}:r pW .mݮ%UoKuUC{^%倧$"\dLb%w^l}&Th. rڸ@֚l0Mb2 0c T1f. Fj%B~3ӆ.SL. "ky^h%Nk(@ƿY?ޤ!v$ڀ4sHN9p(d%fA|G::cVD?dnq K@z1O[,Ȁx|81t 0. 2ߏ.*ӆg&ۗTHfpuZt7ӽއ&JM2[w<$'!_(-|nWI^zVb48OalaS1 Ul̮C#-(ӛDնʚ iEj|9)yWB;8l=L;DbrӞD_Ki~d]]٧۫8Es儡gUNv[Ԕ ߾t@ҍ k"R)p\uhh 4c#`59 G->{ydhAJѷPJTyR~HaB}\<;yVVqsȇhBd,:ic0w4O@m!arԤ.$4MDm1:`n[H=ZV&xJPߓ W˰I=Ƕ;N/ܛ4)OqF@[a.*ds&kZ0[!.1զ5?vN:zzָuq-O8&&h)'*0C'D x&.T~%oqa;! eCf B(W]Ub[nSYk 5ZHtv)ba8zn*|pz5q=Íc9_4S SU.1"'&~M|FldOBT꽊ne6?%ʍX@x[O-S$f;_{8{|l٠Vq|1I.aU #Yd]K*hzj&sS7R YK8ae%^H '\I1hIyq@/EN2Rt)/iU}?PƋ8/opRs5+18ܠ2y~w5ble2O%TV3%i$w 䶞\#ȡ_9ܙ5+VВJdNzLhf-9xS@ ׅqeTX(v-QBtp%'(w'nQ1.#lҷ gۣdM< _8L,5Y\+6ӒkdIC3&=P&UYø]ftjMQ`&1ha;Nc62%n a.v`@f(z% " 8bDKCE=Iˡn&=еؘ/F7< 2|ng]'\wnejBq)j #To'ZX!WQ6ΘAL;{ߗK@`Qݸn~M1ane-_d}J>M˦͇o.f.$-2Vb :LڅOM&r%h)И "7ëp`E0D`KH^ ٽan~)IYEp}XA$ ^~ZNRm`>٫c}BB!ktVSVU# GX7q2m*l|QXH''⏢ZKxb+{`CG9R0V95G7s'AWgz:t]3eq+ۇr̀* }pZ(*-H0|엢?w&~7  .@;A/8r6gSJg% RwZFėyPqQ<ua#Wœ(?+>+GOѷzz4&z}uv?ױJ+l c}@cZ!DB2jJjR!2l<WҎw#֪ m;\`UܢfIW7ЯK7jy#\P RMB,ֻ?zu30.fgP6LOdj=]It6Ũv>0ؓ SCh "^`)[3yViDtK<>^}Mũ\@(Gg^#IYc- %=M :Cgtд{#%Rv)b:\,[RSNw%>'/lܺ(Ë,D;dH9_f8%nT A [qsdk"f(2 5q.j߆tؒGro쫜e3fLSǜ ˡU^cPj(; 7T`J'/674rՇ,:oLAK}^-2#gdHmM^e.J6y$8ɾ{fwFl#:|őZB|S~։Ҳy"AZSTEHI$u0󥁡\h?mkY?P_Rʧ,wfmmMGѓK02=d/昤x?Ay hX_NxI??a!4W;3 l{^"b3}iT]Ih9c>MUIү(zmA5K_Or$-2;} Q).fCk[(Zq/v_=*n\kѷtv[GՑ9"?E+uT.\mH6%!XsUꅛTͥko./OG2J@3XkZQ!ԧaVwxmyę.K!XK.9qWoO1u`* Լ-TOjʆe_Ɖ:$ |"]|&!?N~ZWGdzRTcG+M;dHBzInwڭNZɰJa,߅P%lnjUΌf>jDsA8-i.8IijLߟ/]*|Y O7+tڅ5|ؖ~>4g3»* 39G|*1H@} YKILɀcXne?,VsNJJ đ<]xiÇn̬LB+T,g^T?z.0ҕƆ~dzc%IK}hNю`rV@ 4l`۷'i"ISue}k%)u}bm=Fù:3pE]mm z!섔 q0 ρu=rE~\mg% YEl3ـXg^۬,IV USIZYm=jO IT΃?*lH`EBz[B3%)/ϗg>LquKqG4il2$ULmAA}g.FLCQ{{i}#7ta$<"U:ak߆{0*,/Ar%LJH~%)rS֗nm䱽mϕqv&zD8ifQKTK21s^q]jح hn{o_FNp#&oC ~$KNҬ&yâ?Zк(JciXy렳fgE"|$K륬jItv;3v_QphCj>Tw5:S5E(fBx_FgQJ&FNR|y`1V*Gmy}p"U\u yxT$'Pki΅ՄG#za v#hJפ3UrM{'.oǎH-3 Hq{s#C(ۆv?ڃ?-9P#/Y,~& VkytZQ8zDىuLp6:uiQ_ǁYGdc 4JKlj sr ^CB Pwk_Cy/34Z>-)_Pkꍍob)'I9E>6.mU0sjXO#0)~($~#9NMo\FKnRL8Á*w@]y ~Ncc(^s5h~T2,985HTw%5'&9ԙ;v"[ ,GlsRz] q5u"F5qb#GdGgy 3猅iax_)\.Y`MҌ \$1 IJM{ɃZa,ub2Fh7 qOt2v 7BI{F)s~]i~ؙ.䍻4J}m[J"96.Ac>KoI\&R)ӼTٚJ``ԟ_`wpmiڒA)ZGZue !\cj֖"}1("ՊB9̥AE='WX~Ch4?Tctic ̮ѱb{5 =#d~{mB\$ Wt$ƜJ8@x|h,"+ųd=lK%>T)Di9,i=wPʼn^2/\7d'TmLgBix-j8mCD_#]S@zF}Lצ?VVGcM;w *zPZ.nS_օʤ-rB/*Yu:)9d^HcQN7;0ꁿx脾ZhmW5'OU!]NAy<6":5W4HWW.!"+=zTCOu< NѳebƆҀ޽++L1j;4ۆlp,kusrG_E0"l۫6PgO~bJOƚOS/|b+(JOh?xAO8d+hQ-^hgiRkISQ6U*Wԏ-6f;k3{"n.: V07^jL:eG;sKv0tߤeS7bl#jSQ .%t Jh/R6 )1Y jJ /l +nAFZP ['l _2?mKA/b۰ͯVQyОHylSAqy37aҍ8!v`HU|Lkݘ' QNA('{n ]3|ihR%h7 *O3K|Aym7j9L.box .stZ嗎QzN(9fkth%bpױ<٢A<$|趵%T(x^+s/05?)w?'R;a?'" ?o*DfI `)U5'w^f_rI %Sj%Q7sHX>:؏BP[|jV<{p/*]PN)TzHGvjpq!` 8l]WXa, RwA<)t)HJ_1PdPP[B\}CT˾(Yev Rt79 (ė7Na+K":{UAq+i~ ӫ֊ CBW?op%C*IL>f~JzfMj)3{4 i/c;1XXw`-}F4KuB7@OZ@))sxpgh.K;k3v\͙@UD`!e,i".65,;܂42lh@Dqp BgK6=m++&ń;U)a`{εI'F,!d^EXEe'?py>G6ߞ:?oD>rGdZ,# o{DܠQ8{4탟t ]iJ{(6,ipw5I{tf$V2^TA]q_b FE{LUK'>A0C v|"7hB+V]cQ}ptWI,gF4`_Ԑg:_f(l}lz.n>ʌ<݌3嗨[T!@\gV9o6b@7' zBi:v#NtbCh&̽aM4٭JaV-'5X@Y1 k0?py*:_%{_lNV;[dN@)eJ( ަb4.J/ErfnWׄ?Qu,!?0Eo@cU 52w i)^ @`(nA*@|ϓr׭d\ j#(C 0qXoޠIerw9z17S\r;wprjJ cV{+?ʅ q˺Ծޟ !E"d?uǗњ7Fs0E@`v<ć oMNӢDC3q؁v_`)>q/ZPq4Hψ4ޖVwDbOI7\ j̈(J$롗ǃ?0͖]c~۲@_.2 ̣X50غ⟷d軝lh'E'v JJ3m=ٿv. SV?f̦±(sͯ޼(v>y'+dZj [V#'уH"BBNdF7B熟b*)1>!ĠbutZX/tsy1ށH|ݺ B 4t!cۊZV WSf᜗:K^7NoЫX<{x}Gĭ/žjO |N7fyU$"9g3oH­NVV?VĊ[J!dX9N(jl,+׬uY2=R}LZpMz GFӊ:oߌ%]#6[ nuC#gl|E{ ެRTN2-"RۧhP2a!2.L\ SQk 3#кy=4?QcqZetȁ&$yCKGzKվ5@ {Hmâ *эhP.%n D)cvdE܉Fq$AH!O. {M*8hH--/KҳZ_?L!ʩ\>dV9 -bpd(&ٞ!_./Y}sJ a4<aZZUN[(&-5 CN,] CtEJ ձ`X <Q^ uo.W3iQR 0UX:F3}mb|^]W.BxF!n@-ZzMOk#S{Km[DJ@)֏o*b2E.P'{Ld\@b&=jwdd] endstream endobj 183 0 obj << /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)/Producer(pdfTeX-1.40.20)/Keywords() /CreationDate (D:20210708112120-07'00') /ModDate (D:20210708112120-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) kpathsea version 6.3.1) >> endobj 145 0 obj << /Type /ObjStm /N 50 /First 433 /Length 3033 /Filter /FlateDecode >> stream xZ[s6~c;4;8錝4i6NiMJ*R_ ,ٞx/1A\shWcV3%ݚ3+lj@KMnQQLpQLhpmȉΙ1Z`L!eJK`E8z5dXLW2gk8fd32kf3ˌ l$1+sU~ rfm&îmؚu0;T0eK:r9蜰BNj}x*q*,gbX.劃xrÓj 9q>9PJ)JAlO̎EǏGpEO#2՘#(3H4a;#HGup aYΝ2fE5p5/OX䑩źGd< k2۹vGr5qei0]*c%9n}j#7%RéIߊ{(bcS:=vTVcxy(2 P褡g~97q<{1rzDG!?xXZ>O)2("WሪЛԁB-}0 鍆 z(0%-Kcaj)#G_ƣʮjH* oPs:S-fCtPs +4]![}؎aиqם N)OG s%TCik4jJQLO7']I%!W@z# 72? JN|}MBKS -`cTSΣ%qFttz$%ӑUޝmPT0,{`ٹ_y :x\k-QI4(I&r߰ӈηs;0&avT †z(z"EʸEd0 VIJ =)rU23 8 r+dPRg퓲-y[/|U1'gym=-f.?Ul҂4Ť.'%ҳ|X-{wi^tTOY:N*I:MgiEڤmL? +Н,co~n(;}CAC t֣`{(85BDqSzq >@m1/lR~jCmA?{}F߾>>r9);mBxQz*ÔAv dIi&' `z>KNJ_K/[OK -UмXN&eרN _&-\ >z?QjVB 5czҔ*۔fR4WPU{(˴R~KzCw&>>淞םdНX6tgܦ;U *#u>BICU`MH 6w`>|ݳg`22vHJG>`r0\ r#4Kmu ^y`էA+ŜE= ww[Wœgn eco,틛 Ų>&2-Z gbG! ܸ`8W^&Ŕ]/].r;MʦYGrzQ.rvk$hV^;,˺-Pi*mxϧwluG'u=yǵ]%0<&7 |Hi AwuzAtujWtTy&&S^^^I0蓢)idc9)>MK4"6ߪq{oEv%dslu`(X>Le!ۓd3|(l!dۓae[mO27Mbl}Cȶ3JtVt]mw4l0!Sz wiS"}ز-W}[Fd˲A2S/>plgtPI?Y0BN=.;w6<[4=e;AND}ojo[(1Q&"׋6̖y7[ڽczU^E2k^j//u\r͋% ^&^YK/q/a׼^^+J9ּͼ  ѓzŢ=>q=z{?d"pB9js"m9jz|Hu\ɴCf]6ӺnJ5U(֨,Zx2+ŦEHU &hQ7Mr&Y`j,RW …CjZ4~!w\,>MFW5H}" u{R![(fk c)&?%-ǐ'r2IE҂lL薰-6U^9&GsD=<. >:[JP"VLe0~V+HnNs}hcf}Ma'(to}- 1$܀-@٠@I>Xj8=0K'-*ޚyYRʐ,ނTMN@Y};/fwj$_i+9\ z)㨮esuK> 0+(9ى6Ku膴2yǺq":σ[)cĞI}yuJz!x^/q6:ͺ濪=K endstream endobj 184 0 obj << /Type /XRef /Index [0 185] /Size 185 /W [1 3 1] /Root 182 0 R /Info 183 0 R /ID [<196F3E3EB0E7ECF0879CDAFF21E3EE5B> <196F3E3EB0E7ECF0879CDAFF21E3EE5B>] /Length 484 /Filter /FlateDecode >> stream xһOTQ" 2" >|"B21F-L4&nca&4&&hbia,j593.3,ۗ19$`TA5rhqvB]NtJb=S PA'.>kf&mӃ 8A_VX`6L_־ 8-qZvԃ~qT\kaBrfZJnalV;am ð ͍h~v^ǟa cɏE-> 'a80^N޽Һ3pwœp#8 Zwfݖ)e<\$.eb \%\D9Ac^M0~D+A>}9sԺ}}mu0{=}Y&ϞҤ*ӦMA# &(MPR1T =Q?V endstream endobj startxref 257463 %%EOF shazam/inst/doc/DistToNearest-Vignette.Rmd0000644000176200001440000004014314063420465020244 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: > %\VignetteEngine{knitr::rmarkdown} %\VignetteIndexEntry{Distance to nearest neighbor} %\usepackage[utf8]{inputenc} --- 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} # Subset example data to one sample library(shazam) data(ExampleDb, package="alakazam") ``` ## 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(ExampleDb, 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(ExampleDb, 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=TRUE, 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 library(ggplot2) p1 <- ggplot(subset(dist_ham, !is.na(dist_nearest)), aes(x=dist_nearest)) + theme_bw() + xlab("Hamming distance") + ylab("Count") + scale_x_continuous(breaks=seq(0, 1, 0.1)) + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) 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)) + theme_bw() + xlab("HH_S5F distance") + ylab("Count") + scale_x_continuous(breaks=seq(0, 50, 5)) + geom_histogram(color="white", binwidth=1) + geom_vline(xintercept=7, color="firebrick", linetype=2) 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 `-1h` and add results for sample `+7d`. ```{r fields, eval=TRUE, warning=FALSE} dist_fields <- distToNearest(ExampleDb, 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)) + theme_bw() + xlab("Grouped Hamming distance") + ylab("Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") plot(p4) ``` In this case, the threshold selected for `-1h` seems to work well for `+7d` 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)) + theme_bw() + xlab("Cross-sample Hamming distance") + ylab("Count") + geom_histogram(color="white", binwidth=0.02) + geom_vline(xintercept=0.12, color="firebrick", linetype=2) + facet_grid(sample_id ~ ., scales="free_y") 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 library(dplyr) library(alakazam) 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) %>% # Vount 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/CITATION0000644000176200001440000000656313752621627013674 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")