pax_global_header00006660000000000000000000000064141633254660014524gustar00rootroot0000000000000052 comment=d1daa8079b8dfeecd2b1b3acef447b8ef89c3460 phylonium-1.6/000077500000000000000000000000001416332546600134165ustar00rootroot00000000000000phylonium-1.6/.clang-format000066400000000000000000000006621416332546600157750ustar00rootroot00000000000000BasedOnStyle: LLVM IndentWidth: 4 TabWidth: 4 UseTab: Always AllowShortIfStatementsOnASingleLine: true AllowShortFunctionsOnASingleLine: false IndentCaseLabels: true AllowShortCaseLabelsOnASingleLine: true BreakBeforeBraces: Linux IncludeCategories: - Regex: '^<[^.]\*>' Priority: 1 - Regex: '^<' Priority: 2 - Regex: '^"' Priority: 3 # IncludeIsMainRegex: 'derp$' phylonium-1.6/.gitignore000066400000000000000000000006021416332546600154040ustar00rootroot00000000000000# autogen .deps/ Makefile Makefile.in aclocal.m4 ar-lib autom4te.cache/ autoscan.log compile config.h config.hin* config.log config.status configure configure.scan depcomp install-sh libs/.deps/ libs/Makefile libs/Makefile.in missing stamp-h1 test-driver test-suite.log *.log *.trs # binaries *.o *.a src/phylonium test/simf test/unittests # other autogenerated files man/phylonium.1 phylonium-1.6/.travis.yml000066400000000000000000000010371416332546600155300ustar00rootroot00000000000000language: cpp compiler: - gcc # - clang sudo: false addons: apt: packages: - libdivsufsort-dev - libgsl0-dev install: - pip install --user cpp-coveralls script: - cd $TRAVIS_BUILD_DIR - autoreconf -fvi -Im4 - export MYFLAGS="--coverage" - ./configure --disable-avx512 CFLAGS="$MYFLAGS" CXXFLAGS="$MYFLAGS" - make - make check || cat ./test-suite.log || exit 1 - ls - make distcheck DISTCHECK_CONFIGURE_FLAGS="--disable-avx512" after_success: - coveralls --exclude libs --exclude test/catch.hpp --gcov-options '\-lp' phylonium-1.6/CITATION.cff000066400000000000000000000010251416332546600153060ustar00rootroot00000000000000cff-version: 1.1.0 message: Please cite the following works when using this software. authors: - family-names: Klötzl given-names: Fabian - family-names: Haubold given-names: Bernhard doi: 10.1093/BIOINFORMATICS/BTZ903 identifiers: - type: doi value: 10.1093/BIOINFORMATICS/BTZ903 - type: other value: urn:issn:1367-4803 - type: other value: pmcid:7141870 - type: other value: pmid:31790149 title: >- Phylonium: fast estimation of evolutionary distances from large samples of similar genomes phylonium-1.6/Makefile.am000066400000000000000000000003441416332546600154530ustar00rootroot00000000000000ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 SUBDIRS = libs src test . DIST_SUBDIRS = $(SUBDIRS) dist_noinst_DATA=Readme.md dist_man_MANS=man/phylonium.1 dist_pdf_DATA=documentation/manual.pdf TESTS=test/simple.sh test/unittests phylonium-1.6/Readme.md000066400000000000000000000046041416332546600151410ustar00rootroot00000000000000# Phylonium - fast and accurate estimation of evolutionary distances This is the `phylonium` program for estimating the evolutionary distances between closely related genomes. It is much faster than alignment based approaches for phylogeny reconstruction and usually more accurate than competing alignment-free methods. # Dependencies, Installation and Usage This program depends on an external library: [libdivsufsort](https://github.com/y-256/libdivsufsort). It should be available for installation through a package manager of your choice. Furthermore, to build from the git repository the autotools are required. Assuming all prerequisites are installed, the build can be started as follows. See the manual in case of compilation errors or when you try to compile on Arm: [manual](documentation/manual.pdf). $ autoreconf -fi -Im4 $ ./configure $ make $ make install After a successful build the `phylonium` executable is found in the `src` directory. It can then be run as a simple command line tool. All the sequences in one FASTA file are considered to be contigs of the same genome. The filename without the extension is used as ID in the output. $ phylonium Seq1.fasta Seq2.fasta 2 Seq1 0.0 0.1 Seq2 0.1 0.0 The output is a distance matrix in PHYLIP format. Use `phylip neighbor`, `mat nj` from [mattools](https://github.com/kloetzl/mattools) or any other neighbor-joining implementation to build the phylogenetic tree. For a more detailed description see the [manual](documentation/manual.pdf) or [paper](https://academic.oup.com/bioinformatics/advance-article/doi/10.1093/bioinformatics/btz903/5650408?rss=1). # Citation If you find this software useful, please cite our paper. > Fabian Klötzl, Bernhard Haubold; Phylonium: Fast Estimation of Evolutionary Distances from Large Samples of Similar Genomes; Bioinformatics, Volume 36, Issue 7, 1 April 2020, Pages 2040–2046, https://doi.org/10.1093/bioinformatics/btz903 # License Copyright © 2018 - 2021 Fabian Klötzl License GPLv3+: GNU GPL version 3 or later. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. The full license text is available at . Individual files may be licensed differently. # Contact In case of bugs or unexpected errors don't hesitate to send me a mail: kloetzl@evolbio.mpg.de phylonium-1.6/configure.ac000066400000000000000000000043361416332546600157120ustar00rootroot00000000000000AC_INIT([phylonium], [1.6], [kloetzl@volbio.mpg.de]) AC_CONFIG_SRCDIR([src/phylonium.cxx]) AM_INIT_AUTOMAKE([-Wall foreign ]) AC_CONFIG_MACRO_DIR([m4]) AC_PROG_CC AC_PROG_CXX AC_PROG_MAKE_SET AC_PROG_CPP AC_PROG_RANLIB m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) # Make sure, also the C++ programs are compiled with OpenMP AC_LANG(C++) AC_OPENMP AX_CXX_COMPILE_STDCXX(14) # Execute all tests using C AC_LANG(C) AC_OPENMP AX_GCC_FUNC_ATTRIBUTE(ifunc) AC_CHECK_LIB([m],[cos]) # The libdivsufsort header contains some Microsoft extension making # compilation fail on certain systems (i.e. OS X). Add the following # flag so the build runs smoothly. CPPFLAGS="$CPPFLAGS -fms-extensions" AC_CHECK_HEADERS([divsufsort.h],[have_libdivsufsort=yes],[have_libdivsufsort=no]) AC_CHECK_LIB(divsufsort, divsufsort, [], [have_libdivsufsort=no]) AS_IF([test "x$have_libdivsufsort" = "xno"], [AC_MSG_ERROR([Missing libdivsufsort.]) ]) AC_ARG_ENABLE([x86simd], [AS_HELP_STRING([--disable-x86simd],[disable x86 SIMD support @<:@default: enabled@:>@])], [x86simd=${enableval}],[x86simd=enabled] ) # AC_MSG_WARN(["XXX${x86simd}"]) AS_IF([test "x${x86simd}" = xenabled],[AC_DEFINE([ENABLE_X86_SIMD],[1],[Enable x86 SIMD support])]) AM_CONDITIONAL([ENABLE_X86_SIMD],[test "x${x86simd}" = xenabled]) # older compilers don't support AVX512 AC_ARG_ENABLE([avx512], [AS_HELP_STRING([--disable-avx512],[disable AVX512 support @<:@default: enabled@:>@])], [avx512=${enableval}],[avx512=enabled] ) # AC_MSG_WARN(["XXX${avx512}"]) AS_IF([test "x${avx512}" = xenabled],[AC_DEFINE([ENABLE_AVX512],[1],[Enable AVX512 support])]) AM_CONDITIONAL([ENABLE_AVX512],[test "x${avx512}" = xenabled]) # Check for various headers including those used by libdivsufsort. AC_CHECK_HEADERS([limits.h stdlib.h string.h unistd.h stdint.h inttypes.h err.h errno.h fcntl.h]) AC_C_INLINE AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_INT32_T AC_TYPE_UINT8_T AC_HEADER_STDBOOL AC_CHECK_FUNCS([floor pow sqrt strdup strerror]) AC_CHECK_FUNCS([strtoul strtod]) AC_CHECK_FUNCS([reallocarray]) AM_CONDITIONAL([HAVE_REALLOCARRAY], [test "x$ac_cv_func_reallocarray" = xyes]) AC_CONFIG_HEADERS([config.h:config.hin]) AC_CONFIG_FILES([ libs/Makefile Makefile man/phylonium.1 src/Makefile test/Makefile ]) AC_OUTPUT phylonium-1.6/documentation/000077500000000000000000000000001416332546600162675ustar00rootroot00000000000000phylonium-1.6/documentation/Makefile000066400000000000000000000005421416332546600177300ustar00rootroot00000000000000include ${DISS_PATH}/Makefile DIAGRAMS=eco29.pdf all: manual.pdf manual.pdf: manual.tex $(DIAGRAMS) pdflatex -interaction=nonstopmode $^ eco29.mat: phylonium ${DISS_PATH}/data/eco29/* > $@ eco29.tree: eco29.mat mat nj $^ > $@ eco29.pdf: eco29.tree ./etefmt.py $^ -o $@ -r eco29.names clean: clean-latex setopt null_glob; $(RM) *.mat *.tree phylonium-1.6/documentation/eco29.names000066400000000000000000000020551416332546600202370ustar00rootroot00000000000000AE005174,Escherichia coli O157:H7 EDL933 AE005674,Shigella flexneri 2a str. 301 AE014073,Shigella flexneri 2a str. 2457T AE014075,Escherichia coli CFT073 AP009048,Escherichia coli str. K12 substr. W3110 DNA AP009240,Escherichia coli SE11 DNA BA000007,Escherichia coli O157:H7 str. Sakai DNA CP000034,Shigella dysenteriae Sd197 CP000036,Shigella boydii Sb227 CP000038,Shigella sonnei Ss046 CP000243,Escherichia coli UTI89 CP000247,Escherichia coli 536 CP000266,Shigella flexneri 5 str. 8401 CP000468,Escherichia coli APEC O1 CP000800,Escherichia coli E24377A CP000802,Escherichia coli HS CP000946,Escherichia coli ATCC 8739 CP000948,Escherichia coli str. K12 substr. DH10B CP000970,Escherichia coli SMS-3-5 CP001063,Shigella boydii CDC 3083-94 CP001396,Escherichia coli BW2952 CP001846,Escherichia coli O55:H7 str. CB9615 CU928160,Escherichia coli IAI1 CU928161,Escherichia coli S88 CU928162,Escherichia coli ED1a CU928163,Escherichia coli UMN026 CU928164,Escherichia coli IAI39 FM180568,Escherichia coli 0127:H6 E2348_69 U00096,Escherichia coli str. K12 substr. MG1655 phylonium-1.6/documentation/etefmt.py000077500000000000000000000042661416332546600201400ustar00rootroot00000000000000#!/usr/bin/env python from ete3 import Tree, TreeStyle, NodeStyle, TextFace, AttrFace, faces from pathlib import Path import argparse import csv import os import sys out_stem = "etefmt" # not actual default out_format = "pdf" # not actual default names = {} def layout(node): if node.is_leaf(): pretty_name = names[node.name] if node.name in names else node.name # derp = AttrFace("name", fsize=10, ftype="Arial", fgcolor="black") pretty_name_face = faces.TextFace(pretty_name) pretty_name_face.margin_left = 5 faces.add_face_to_node(pretty_name_face, node, column=0) def read_tree(file_name): if file_name == "-": file_name = "/dev/stdin" t = Tree(file_name); # ignore any errors with midpoint rooting R = t.get_midpoint_outgroup() try: t.set_outgroup(R) except Exception as e: pass t.ladderize(direction=1) return t def main(file_name): t = read_tree(file_name) nstyle = NodeStyle() nstyle["size"] = 0 ts = TreeStyle() ts.show_leaf_name = False ts.scale = 25000 # pixels per branch length unit ts.layout_fn = layout for n in t.traverse(): n.set_style(nstyle) # t.show(tree_style=ts) t.render(out_stem + "." + out_format, tree_style=ts) def read_names(names_file): global names with open(names_file) as csvfile: namesreader = csv.reader(csvfile, delimiter=',') for row in namesreader: names[row[0]] = row[1] return names if __name__ == '__main__': parser = argparse.ArgumentParser(description='Render a phylogeny to an image.') parser.add_argument('file', nargs='?', help='Name of file with phylogeny') parser.add_argument('-o', dest='image_file', help='Image file name') parser.add_argument('-r', dest='names_file', help='File with ID and real name (CSV)') args = parser.parse_args() if args.names_file: read_names(args.names_file) # parse output file name if args.image_file: # split arg out_stem = Path(args.image_file).stem out_format = Path(args.image_file).suffix[1:] else: # split input file name print(args.file) inputfile = Path(args.file if args.file else "etefmt.pdf") out_stem = Path(inputfile).stem out_format = "pdf" if args.file: main(args.file) elif not os.isatty(0): main("-") else: parser.print_help() phylonium-1.6/documentation/manual.pdf000066400000000000000000007266771416332546600202700ustar00rootroot00000000000000%PDF-1.5 % 5 0 obj << /Length 1515 /Filter /FlateDecode >> stream xڥWK6WR@".rj4E[Pߚh ˢ"Ru}g8*97 J9+˴X+!2VbUjݭ>8;=$h@YX\Еڀg6SOnc1y4Y}֚I=F'C('GwNf:|<3p$bQG X7u4.|+8z/5_%v*EF6N2^D{mδ1[\h-ѭo՗Y hQOֽ%Zz7;O] G%-GD{M%I2AzoL*c `y,la-: QAmQG~dT5S8*g%'VfvD 9*bʆsvGZÕa>nDL|;Rvۓu+%"J=A4dDq>>٥f`FMP 7 VFXΘ޾ (7(ׂ1J<(.'ߒ\ʫZ3m_;>"&,^d@lqR@0HA{ȒآP%t џf6NS4c+,Y.)GDYxv ^o:hm b-|(X^/x3z+!5,Oi"9MLoprkn:} 3CA}茲=](@Oqw ރI7cp_Q_3|\2[7E-IuD:?I۹((b7nbMg" '*B h|tq%+]o!C&.>DmZ*p0 >2ISjY #;2yyʂ5/_~/ endstream endobj 26 0 obj << /Length 1730 /Filter /FlateDecode >> stream xڍˎ6 QJJæ[) $9eDW~}ʶlؓp/ۻ-23/VՂ9It.VO#o Vr3tb}'L9 Hf$#zgaJ$_jʡq 3NIsB/Kpw߂ ,p/[w^a,J\;w(TݒfoXYL<\4$IZd7gm!4A}߃aA? JXf_[QzkLo&3OoPq;g0܅> K>XT˦M h2:'c_es @AѸyvXKYy$>Stv;Ma.9 nНP^6_>F*vI);,"@kYڄl*ɐ|g영iz2#01leR0Ukbx[E ދW\Tyf9:yRP-!TȘĄ|>ƪYG3i.v2G0nQJ mIx[sjڙ= 1Z fh )8ʳhp$H xB'Dyuw>>ʠGL -uY]xZ]bDpgKiuT\ ^lOP˙dʻNh0l7nlD a8H;{D-?Gpǵ l6'*ZoT^>R[+ w= s'͐ei4Rq$4Y)ص_ ~c.}0Q[9n$vÌJP~Lh?BNytପјԣ)W#vh֢u5 SLYْnMx*6:e&Սѝ1#jEV% bR#ɭmcz0ZE:1sօx=ˈ155ƣCǓK!yUP7TS? Ms7$ñq97.etOf?̜ղulOW# ͛)|vcxU:x8@Ubox`3%,ЏY w=HۘԽh<荾kfdfAvM6Njh%?<:qG*I'>aߏL0 ];8j(GB7B\7^~ek3ǔFeaS4qll4[RrE^; ];(YId08Γp endstream endobj 54 0 obj << /Length 1959 /Filter /FlateDecode >> stream xڍXmo_!-"Z/j 9$+4Pwa-Q^E]E9^'1`gÙgfb3Gzv1ƈfrf&ڱzJy7v9fwj3ȷݕM]9,-ַn#Ա!o8őbv̳t*@qOq}yy/x?uN@qNKAËgqC O<g!Rzuqd5@nSwyQHE.>cH߰Up&8&s f"Cۢ'BSbC\2qm 83nppBȾLݩI͗<U6E: ԈORI &q)b%P4<\6g: VƗ@jYԉm1,et2!bq{Ӕes• /\l*V5atL?gQ7ÖOj̃@ʦcA`u#}Cr p>xOuDA뙍$cŋCކE0}ׄ.@Ra|k*a;!8;%d~X@Do bhSt2MyC6]ۢJMv=Bp=(lTM/f)8}IbDrs Q(طa8>톞!z,] h)}H38V=C$a[;Xԅ`=$y Jˢڗub _Drx*dr )ۉpgS&c 0j*4{O#;xs܈zC4\fؙ0+q]dVF2a߽V% $aŵ\J&-{g-p&QSXe/L-_ ҭFíAKoe)1Eo[> f\Yt9,2p!6x.1N_ZU3M㭓h&=3LךFNݔO2c#31o fzFGEA,~ FeWILmmȒӑG@2:Mt*)CclpNr q$_l F;;EjOcg(g.y:(y(ǜ 縬gRɉ$cΪ1 ʤP?n endstream endobj 78 0 obj << /Length 1196 /Filter /FlateDecode >> stream xڍVK6%TX4"S[Aڸ!A+˖==9ΪE`$Ï3od 3(*ꛯ73{bӺIYK{;xƌ/@l*Xb50ƈf2PD%| ߖ (抆lh S՞ MQmy+K|.)"YweV).ڪ]\̫mzP0! 3fV:_э+l h-aJCxXh}ٜƉ&b:m7hʱ^1Q51SΛ8<me @:C7\ص~L;r(w#:k-  1;p*T|%fG T1uߛZ.fsiͼcDщ3#rc6D HᖳKF,v!@$*n7k2M Nha,yr1O A$dI$e̾mW' 'ߣ$q6e'⦑~Ȃ28|$X\SfYmYF!*kD'HǥEk-I&ZˇQ( C)6D*0eLZu u'9rr%x ]Yڸ8^l@ ,4{B.K;~EJX'ݗ5KER[=%/=T[ٗ4P#F,\ܕpI`G4(.ltZpotZ82[bJ` Pp1*kϟ1UY *vdg5tOk4/N^+]n*N++-oPvhp O[}d(+[ֶ_<gHc{g`!šѫPI 1ѭ >p~)bdziBmp*>uPmDte.鄋a8| H۝+e"]"#rv_S endstream endobj 75 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./eco29.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 89 0 R /BBox [0 0 715 436] /Resources << /ColorSpace << /PCSp 90 0 R /CSp /DeviceRGB /CSpg /DeviceGray >>/ExtGState << /GSa 91 0 R >>/Pattern << >>/Font << /F7 92 0 R>> /XObject << >>>> /Length 4545 /Filter /FlateDecode >> stream x]Kϯ9|?  .CC`#1 EDJUUz.J_ɏEX_wI(럮nϾ)&#ujκ`u *_>Iut/_&jf/~"3 l2q¤O.H4JJ$S4(^;e.+8 6_0$+\m( !`#hoi3R1 v*$"A2_^)֞dAsAuɂ6+|Bw•%[5;ㅵLI Մ8 1kEF&E)/4 &7Bm{ Q4X JGPQDsrB *eBJTn(iE&UJBidބa !8)ĴAdjˍsTmh0ű hFJJ+td#Œ!U d77 -4/4:2/IzАhti 4HtX ū0xx+*sS5{='tEk]_S"/Oql=aw)1CĂPD< . 6g<}*CmI}j{_K| \>ǒ2aUi('<< j\rcd];v ư*<4;bL^@Hy(OJԞKiĀ%6[\YR9PÜ 薢#zn\=-k ̀ KlR)/wuh&|jbkQީ<.w`R撂Z<kIz[Y԰ct3Um$dĘ9"5 m1tH8" KN5q_C3ۿyN< wp:>L^Lh9 M{AW6 hja<'giB䍓 `B-Z߿ă3 }M+D̙2ZLJsw[=&eғELFcJc0u#-b_׀$XvF;#$7;2%Wn.:ň=YYZ#W|6MK1+) ,Sc08(i pGK|g6H6 17f>Z*>`]t{"զQ2?.`a2̀K3GZPf .o.zNٳ+M ] Yke?܋n)0'*.g|LDO{\w׶ql!s-ڟhUXj[ʔ &=*x \=s xodgUvH$T6]l{ #z R4bDO~>y_xQreO._6=yuqKpP+*]ubKjsq;?lWh Eo8Т8 }w&bL .V.~b1[ {E/*>dhͤAbhK}:-3q8(0%88%d di!S)諌Yɴpk|yxp2 @ ^tKQ=4zA>bmLEuSNxV6AK׀#K>㙵Ax{ oL>'=4mlI%k.B=4 R5, 7Oz셅`u|8H&Ew= `"Oh#-[[C\)xIsr8$La6 h]X. E_N$$dи;QO5Awy!z=4bNhm >5c+i9]tH1ܴ 2!>Rl! YMM¨ѡpKY)W& H}tUdT\^CB\5XkkЍ@{h -}[CioDiq2 D?-~tKр=衙HWo>Mn 9׃%ಱqO=oz4J3$CY/}~sR3&7`_j>ҢKzQg:f }GihQ0(#T rS΋bwX`HA_\01H!9 ;##`f1K!ÌA M(V)֞dAs'Cvɂ6z gڇ %[5;xĈgHѮ F Fw;=5Vgڄ9Z'sd^]nPQȥ^ʻY^nT#ev\"H(Yoۣ*> endstream endobj 94 0 obj << /Length 735 >> stream /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def /CMapName /Adobe-Identity-UCS def /CMapType 2 def 1 begincodespacerange <0000> endcodespacerange 2 beginbfrange <0000> <0000> <0000> <0001> <0035> [<0053> <0068> <0069> <0067> <0065> <006C> <0061> <0009> <0062> <006F> <0079> <0064> <0032> <0037> <0043> <0044> <0033> <0030> <0038> <002D> <0039> <0034> <0073> <006E> <0036> <0045> <0063> <0072> <0049> <0041> <0031> <0066> <0078> <0074> <002E> <0035> <0054> <004B> <0075> <004D> <0047> <0057> <0042> <0048> <004F> <003A> <006B> <004C> <0055> <004E> <0050> <0046> <005F> ] endbfrange endcmap CMapName currentdict /CMap defineresource pop end end endstream endobj 96 0 obj << /Length1 20828 /Length 97 0 R /Filter /FlateDecode >> stream x| xם9ݡ_ylqa;S–,I,K#[X/4CH0$H|r$M!)MC3 )ۦivM^bϙ=~ߵt9aP*ZTYlEs'vLujhvo;C+BixQШ 07 &SQ /EUwހѳm4õXDhB5wсڎTAHSي{S+fF! ӪTjQOevL85[6t]3s)>|˿[ Lirsrj4 8'nI~/!8QO;5P [06/7E 3Rc Jᢤlr}pƖW68;K][|SbF]vv-ZXQܲ.~j4)ޒw?M` YWY{`*w g@ .mʆ4"aIe uOЖg4xdvE3۷Y=ڌvWɽw,%ؼM Q7_ڡY"6Di !{d6' R[-6RB%Djߴy6{&@rRK}:MMs>#Cs` 0Q/O=}9rڌ/d&L"X#;RYRZT:@)mR:YŒ[v:v$"<'o;tV*փ?44&Rʪ `\lk5_#Ny/ n;:u~n̘vy`ʫgP $]g3R)s:ѽ|y6FRvufEIũ;wN*(p'( A3?'3xU\8x&#{L8 1Eήv?>xn޼;[wގGxkkj:Lrr*NN,Dޚo~|p\48'=~!% +4`pSMȖ#.Edg+!)p ^{{0^G ?M酓 qv#EV?nŭv8veE\t`lN~~7'.$OnO3 G'':ѡ)Թn-.ºukwB|ǯcЦ+6$@A|H Q)bٰXl̳xނ2}ᢐy jU>hI׃g};YeĻWD6uTzkFV+@ z05lCFJQC"DM:Q\ql_裫/=ՏjnO> mpɶ;=*=軸 w}ϪR"|>P? , 63k7Xm6 6+ėVͬ˻5˟~t_\2ڬOU+kƚkwgl55nO+&4:oHIWK~ 2]fLx ]L7f$sWjp9)NOZiTq(ΘYG=#̙Ug r=Z3b[k7 PXuk)/%1yf;-Xh @6D.v*8GUBJ2RVdX*>},P?pV@qn~X{) jب Nj5%ZxOauÿjrxΖ3%?S-KﻛOF:?ޮ o279gkk͸#2zu0 Luw0Jà`-&UYbX AIQ6ߩ5MG=˛xϬZ8w?⿃)`/XS1]2{z?7vG_u1:ڮK/M~S980MszX7zv/^> QkzYJv%3q˫Ny^k-7mn)ԥgg`!;fԨ^qt)u9NU卝~`ӳV6;)?p!j$i7r'tq *F= U8Oc~*~1Z]fx [\~M44Mi~ O5V5 ze1Zm2if!rJKh\|ٕ_rc!m{Ƶ:>Xo~h1xvA0H eAޏ77>¯8Q1a℉O?lܾ"g̶?|zDj Y(i#ٷ͟ ?gxRPIh~~"9l? -Œw;Ze:g_D]?3p&Oh%iJQ.Ӧ>iN=4ĕs#?w fwn—evs[t$v4]!A <?wbaT H !͇M44I@_gFExb{/z걕%?V5?/Ov0~$a'nj{'m*~ ںg%/WB$.%osQs(f!,G`Kod=i=rP1gE?s?=d̯AJdtfϤ iʉƷߚxbg⿮F.x1n\P/~iM8_t@l9,^.?4Z}̥Dg"Kh%̓7OԚ04aO$@k5ƞj|Y*4 t*1z?-1ɒUHYw=CN`%',-])w9sN8+wbFN]BM,< ZI݆zV^^z݇gĮEsb]IXgIkzP>; $=Zu1^J-ӒJ7a4^+ K\:8,w&D?8iX9ܐ6PtsH?D<aOtCHy.cB|>B >U &HXH:#:w'4+OG!WU/h/ž#<{ b[!7l8/y|>w_Sx/E/%a8>$bc'^{{?ng:p}P;p²u|<ƇxrAuj6@,ps `C% yYXn΀/ɂpPyRHr1~+x&=^0="p';J⃡+)DICH* I&F:@_@y3E\6!&fR@J1 <3 C2 ypH.ST.O!^BtޮBX"J!:.WZ0Ja6j-Xv\7:]?y5}+A܍Alnb$樂f Ѡfz%NoGJvz%V ؛[+^^M nWb+q+ Rb&KP!Iܪv%%6I\x[&neboi-w-;enebl/2v}Cmk~3tGtGlbwPwq7gM X^D^ᮣai;&N> o svxѳ3[BAKk âNOjia-X$gY0 endstream endobj 100 0 obj << /Length 1776 /Filter /FlateDecode >> stream xڵXK6]X>$QJNIڤ)PE\ [%=%˻zQ'y|^o~zŋq.ֻ)B2fq*|q[}^@qM~Ĝ/4޼%gZ%<ҳw微LyZ!\_*(LS•֎*)*c4,;a"U R{r[f,QC-=wHfghw!ʲn iVb9ȳ.{~k.B*@ɢA^2ˬmMApew4:gmCFuQ4Ʃ3;2 2LXBp(hf @9{.a0dB0@ `벯?]F lmlk 'H7^\Qy\_E[tJ4{tJe_#,#C7B6qcAVWH-ؽIZO."DkZv8Q;r!g@Xrk+k!%Sj{@0Ǯmd~G䷴vihѬҁ!R\ZQ"F3Pa o0Ϩ0|KbJϦ<>嶠i9w#kS޿Wrx`7 yTe.8 UT) Գwܚ4*2OO+tx4GDlVB`I>&Pˎh9TT9J+veuQR81wc4d=P~T}~\j"X=pl O4pH e)F*՞ xy%UG̐2]J*5?, HЗZT3GХbY+ƥZ(DZe}BʄQYŶ/r80=9 aϛO3+weP ~`)7Tz1#=|*~du>db*=Iϫ${#b'ʣ[B endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 790 /Length 2025 /Filter /FlateDecode >> stream xڭXmo7_1j #׉/y>$Jdw3ᐎ,y"9Δ%;b q@{ .\$/s I8\!IRP1Sx0PKEG?t[yZG6Y9 Ѝ\qag\p0ʳ'>&DP`(|!GEpK: #6RQ l c$0ڰOtȢcϸ@[  .HJŎ"Y,P$'% g|S<FХ0-D zvˎ2$qT2>H9@2Hm =|RgLEj :dSBJxC%c*mhoY%-~` $Ġl>0D࡯p :+X]AF /Bv@0㐠" B 9)̅  gXD5ԼN:jcQs0s5'[j~\eEQsԮ]a oEN-B !Jgr9eLMR!ϾtHM,JpkP_u(t1ױ հJ0!*VMb։!VAIu"|%e}b}Is}w@IO}lcC>|{d~ oUT5 Q &+XE56H&:u%ѵ +Pud+|-aޕ:dD'V։\bo`S2R<'T)^%\g* 0멨/NQ [[t>F~xax?j^tv9tΚaT?gja/P ,aoh}u%ܘ;/Gwq6C[<{Fb*-#*@UĒ> fK3zh~-ףCwMs9ݼ5y?j]7_~˓eu:bjPŢka15ÖTސu&F3[ J:l F*%6$uWƶQ,2*^Wb3mu.Lm1u(̷6fncxJKaKI/ݚAvS/„e;6 f.i68kXZpmq-w9f҃};[?=ofW8}.~ٴ=zlC?{ _4Q3C_c?><|5SchkHhg'vp4,cd5/lqI9Fi8,zBu-u17-٬vp_[`?pqёRUH;m KZrW{WV:W˗kl۪Q`TVK$2Vphnα4I%a?mRsSL횕ЗJ fL`x/ qg5]X2Igg܊ o~y3߷>9O.Ef!=snL"cڈʥ G(d>xFTO3%Q3%lvv> +[fuf+o(thqHiX^$٢2֖G  H$NkꜶoy?Ml6)U6XHeyOzD"V▂P]!QTAr+a.-Үf~xDW&OZ>>\K~)J)QyV,cmXu׳xc .a,T5XLrRūu깏0a[X-t2 tZg$ ʘMڌ?m{1W endstream endobj 127 0 obj << /Length 494 /Filter /FlateDecode >> stream xmR=0 + LQ4+?o0j j?1b`Ӆ\y? )evI{ K&li5ϮR̀Z0~t"4^Oy[U frGZ4밇jgTĭ? endstream endobj 151 0 obj << /Length1 739 /Length2 47984 /Length3 0 /Length 48377 /Filter /FlateDecode >> stream x|cp-;c۶m;ձm۶펭cg]9߷ԩUuԘcͱ֏u+ 4sgf`ڻ:ڛ)9ӫY;IqL&.f@;8!m:b2rbwj\ia?m̜vNfƦv6)ػMT :.NƦfN{sPtv1uHۙ;9;vK3_?vN2N{W;'3;x:-,]TdabfחA P1s6sr'%m6fltRT bbf KM,lL ۙ9̔읁.sj@k;3ggRfv(8@;ifgjdN+\<7>O?,Suq6X;{陹Yll f&&?ߗQ7 oUzVK=V蔘V;18HUF+ % _Kxx 77Y !pu&pjb\AnWKǣ6w/[葧sh &#F:gnY~>K# 2P|lV X6eVX_/pP!I@U/jS!W#f)LuؾYN8+uhDӻcd΀׆{!:K2t@a~PBؖm\s" UCxiuS϶0'lSy1T.XnB&}4ټ_KE6&1X+,ґ‚ @63z1u}g+Ȭ'6=b}|&m[wLPo<%=P][.y"M#z_Rؗ_)a]eZ9ps^J2!XY~.-2fA)h2F֋, ";V j>Ob"|tG2[*u9fTe &J J)51 لKÕ~Ks+kH\R詛~ U7נ [Ķ4jgG{y d,0͓i[ Op+~nU&ȃ W~smJN+z39ʶSd *_]()0Sk7JpF7nr E&Idu^vpε i}Ʈ`['ex]4E' _S䂌U FS|8op9 #*̖~ \5gl>Wz-FIj6+u< @# +8EAڐ ?|XyIBmhMY)9Av"{oFnVdc-]_+T2m$R'V{NIGG@҉7KD;XY/fH̠A b \4{Y>&u  abXL_*_ie-ؐlsDNL l}`%TIj˫?X]j;AIB/9͒@4 $$KC MTQ ?,6jc_*Tz %֘3۶Yf9{-{f}ծ'՞g))֡ט; `? WWYo$-+>s5GM4mV)78<ݝ4\MЪFf['NĞ_IƋM]*#fp,$D#qֲfRQtPTN9pT!|~v2sFEe)w,#?Qeb/p,m 1$eo/ jI=hp<.ͷTew=04rX sY%Zl%ɪ_ 4TT;H~|&49įAa\\;t4Jx*XpDĿ;4chҘN!0AqB^7ګffDl)[Ee45 L; 2"ো^,v#6ئ&WL\1(-cn~J.).ݻl1seĕ9:%\NDVe[Ya/˳?)5CY+PE 9O;xJtgkzި.wPv}˛o{4bOXa+;TWVH [6CRD;< {+e%!g,Ez%j@]N4VSRzti=|ʿBLf˻HfXV^\MQ8"k6D8iKI+&pƻ)awk\pJq"xF{@ESDր\j6WzF\Bj `{ƍPs7ՅRjyurM4gk ɐednI 29>u=ދ@4$T#|DƋiz r빝IIc{JԦlEd8k+lf6(;.*?{Y3kw|}m=Ml\eJ1oŃغNG[ N95~ּA-vh}t}#R6:!sH' .+r(Ql֪ 7}Y6Mrc׬s~=7& a qhdCĊn`8wR)N~hK [4t,,Sd9 J רqwlb!x|V(c< tXCSw=2>`R:E84K< Ys$fВݖ5n .A\2K%YAULIQ9yʘG P '=jo>7|guiT<+(HszMQb^y|6q^(&"v?aq7Y_oB_HrWp6pH-fgYIuLhqCg 1N;/U/;|Y5<ڄZ]֥{alyKRNM2x DB xmpۍdZ1z!g`E1ڱG1VmҀ$كq2g<3<"yuZI?k4˲nK!ܞ='bMg`xoE+`kb >d mlTȴ뼋H pJ~W;2u3dŹUɆ؊姲GVم8Xq&'Fi˥$CfG0xL !ډ*I/ϩA?;ܷ& !Y9pN'o_:&̴ϧ'Eۘ# j3+fW| N^H$Ö؃U+W>hz6_so\BhdX0"3yOEavT&J7+< f3C6%O*SNoJz%|Y&Š4RrB!s1(?Fp'ޢuH:0_8\Dmwb\4Hؐ$і?TV..&?0vKPX hS"^h6̮aAjdmIDJj  ͘h`{ 5TxK&T3Ia9䎙I,!4:7bd=񷣝BRfӠhE1%X'c3qQwM&g&xfMBؕ% _{s}7';z(]^L5:3Vf6}3lTB &Σ@Q5#| (9erϻrFk5݇z;jMI;0Ru<{y^Q!:TB##.}?>snT?A uBije%`ПƗ"n$bgT^/:RD Jd3IJVW1[{#BEifٌaنrL#L⋓>FX$Q5Dl|ΉQaObкm{[d73 0 JCFubV?gޥ};A7#y1[Ɇ(q'9Έ5/.]H/m*5S `n1hC-tJ.yHxxlTc+_jє"4(~+#ZԈT%A)>kaRFkdgBh0@Z)ɬw;T'8 j.7M+^D>skeުT%Y7-yئr&П,\9~R,cvvx$Gn٤NN,I^e :<QHl3G=aDpiUfcNG_INgrcLׂj{}]y؏+X!5,Qۗc_͵FWpK3RLȫ,8X珊e8WPj\0 [D ]!LԸ[W%2R;LD WM^^C=e1΂w|fl_x9(k[d;! tױЁbUHju]>GR/c\J:+=oJ(=iamnP̱1NqmgӍO N• qQfb5i2DnT iNC|mwChʊ9s4.EaaX#1E[AR}0F hm=,&/ )!j:ߜL3"$P W# [?iKy^Ȃ"--L߳$:BۓfVXj3(VA{AG0%)=ԀI+|(:q}sʼ[[9"ίR(v-I|sz}i,KWiR^VF?ʹ۾HVQg }*gf&9l`KQbnmw[j=݀B9*Z<b*"VSҝp޷|L[~(n(ޕJxΝӒ;t]S (,۫qH`B1hEL :d|L=_J%^d;OAp=uU8ڴbt݀U/F6Vm=5>"0NO!>Ջ.D˝G|++z@ʀu+k sW7TU%~{fZ~1>Jp)`{OJy7ף? N3!j<-˪KHX NdL6񭁏*/+"RèP2|hUAb/s}G[qJ+[o-亇F9Ζ,qLjDI'׽/L!l?\x:QS,6tHH2N#PoVQ1bj92?ȷK2nDNM޵SJB&1/٥+.*iBvwLvߣ ]H7,KP]/M&cX`R,[33hdw3(oDP rUh=^l#Yk\[-׭kV K^f C}yjbbW-w]+,F9Go. WGi,ik@t͌ BBƅ3vlUToήn*-~СAjyQE=>6XTu&ʵܿѵ|u))[ #['T%t1clfgۏ.qmf!aKEV-?ÌQk:[ڬߗ$v %boi񃸞˒ws ǖHJp`]=t8V g,Hr =tz{(@&_̵9 Jj rCL KPCnQ7dr^F-ƉFy5~}( #G? tA!u~lŏVyGVKۇ$2U.Mf ` P/`K$=nA.,pUtkr/L]M^VC*DWŢFtp:Ys|Lޫ˟FM6Cw=ɤXw(^&{7m=ZANVZޙ^}bT#DgZﲦ#S4o*?jWj"Ur vKbLLd'N`ƬE<(J(f;HCj ꏥ%688rm뗊bG&Q/MFP?4 W`Yre_[0cQ1/A'6IefN|P!Ppj R.<7&^RM[[ЖyoUyU5"R[(r2()"YC''G-)ʓP U ;2Wg!ĶzXB}HĂJ,5Cx*ԯ<("w* (if}YZ[Nw|׼ʓF*ih]֕tzDBФ! fL'ʏ IEMPRpxLGx?|Ķ>. +C'G|ZBzp~xqriXg*:卤'U;ff*=Sofrȓ?!PM`U yPG_˰zWxv\UUixaD <>9Xe~gCM qv{_YPV5}g+XjA?:ߧ!guKil;ǐ5^ }h k~_Oe=!6K0Lx kMƉ|+P=X.̖n2_XiuOUv ; S2B Ɉ|NNj Ks/ǘ.q@ݿwHϡ-|rY _oGޜ 9[X}SX<3|'l`z+";%_|db`jFds.1&@툅$ԱLv{e5CXġin۠w<N$ GG%8`}S@dx 3ZYYⳡo=P i<s\~0Bd?`!R9v_gvmE$ A$W)Cd+cה<IR<^_%Cy- ߤر[eo-%ZК4 pbS :/CyRW\#W&Lcū1'5ȹ 75 qgD@eQH6tba7eg|c\24~F4zZ]cႫIZ@0%[vm:L@Q\ui+t=T%H3Y:O}_]0nԌ)EzcC@ZS."]Jxg8lUewҋ-mrcFo΍ rQg6>e֪T(rb#w->Z>Zm,>\]'&,)?SW]& E:>fdt;zq^25L!.H\;WڟaNg$Nb&CO,@=NcE_3 !q83N­6d/XFxL~:iOtP* ៻hjMy X"?i=n;bP_q ޷u5ӮגE^M9箱'fϷSgUm߽.юӝӎ/WAc!3s1w܂3+]Z\c՞/B[2UU %>#[rzb< ϲ/ÓCuxAf.SW>7jfreG7 8 XsDRcnkGIZbn#/AdtiiL_pN7|{·pzJKpͮBF] tEjkn˝T"%nAז u@^'*﷾|0T-S]۴]&.V$. [& ̔iP3($kkbpoi Pi(lxy٭r8RTo} -0K B.[+]2 L;XA Kvd/4KgNix]ܻQ*ipx&U -|/m2gX!$C<8؋p{.bxi=6aQ ,Ip3>YILy"6>z59~$~ѧcHSVEYe)$Q6+6[f%>6[[l#S ͫ-J=dڷA U87;_R2qh 2~+b>ٸG73GuG9Rvtw^T .*GЋi>tEKȏ#oSVH-cmzB=Q>ţV ӻ)fX7D*dw$iCr il ;ljTCϔW~Y45ͽ)蕕{>+)Újs#aKYQ~2vçVY>F}oNzy@t(mUb-u𵆭|tj!61{N-mp?1ǏSC !8^]{<Q2Ҽ^tlQk6aBt썺pݼ7i mZqBapɳޟ\7"2rrN%6 Q8k”avc &5'ڬ 2k2h R4Й`@޶AYZL95Emel$z_D/ͣ,u龎dg^H Cfy s*>9j=6Ypl X{F&`rDLqA%O)~̸햛)rТJƷz=Yg.yu7KHpw6Qmz]4n"QS$U/G/bK\EF03R8eW44{%|Ncq$؇Q)gABC.֛)gƒiF^+ @T5Ctd 2g98gJRlK=l[^Q&ޯɽg9aP9SI{ -7Hfrb;6jWFaύZ& -[yOV*.IwGh(5){d#Ͼl_W-kV,WeS;71b V/h5)QV֐ĔeMBYPkq*N(D96Ԅη>nIyajVMEgpcTlQ(m ؤ$\u>r^$\u0Xbn(*ڜ ̡)1;M!j (#Y9Oy~4C(洴zx0[ EYET[ cxjs{Fئ@D E"1x؞|=G\-Q/rO8Oᖤ{ml*ց ,>fm(ȃKKoLdx8dr+K8$ sv)ؘN|,8GA)aDQ}|{So<\2UļmIAa4J <; :LdOb2"d+Umeu,xَ7qv4:UB0z1 WB6@'HB/ F訬"Lӝ{OW~!jz';&TTH] ݤ.&׹ E,"gPBvdaluXEX Sy[sym^Vǿ) [f 2"Ny )EՇ̊Q[ETF%DAqڡ5&-~#޴e7-SG njm#qDk\e^ A6f**H=µyJ9(/OoWDcסĊZeɦ9p0MkLU)#BJm6b%un㗄e/"Q³%X!JyH;06F'B"=~ lwppKc4>჌ޟء?80{K;GgV)E8` u%^x^Y{6֖N9p(dh&> ,F Q mqY2<9ܙzj=f9A?1$b% ohEaG=f,)v}##e4PV2˳_e*rG:G(U_3A$wx?Y,dX#< _в9֪8eL_j|_ܪȚ DNQ1h T:e`eO5_ɳ v#%(.6[1je^ώ?w1qIVȸrjfbJmjf!Ȟ~u^nCthe .hm˂t;ƎwVn \Axly]azn 0A$x ̀G8V"Kev{mOMӨXfrқ{'~nw>Zoc54I~I b`[%C"BOnE-ծJ[JУYL x{>{=uPU88A5;9`+#JԘAԬ>%ی[|N3-vs Bklk rSy/=L*>91`hd7,.|t(FdA!N6U6ya E,*rx)u{H$w,^lZJp/-7&6^qQoY#?YҠH-;KV,A>̈)EA% ^]9p@L w0]V5^* =c$?7jV[BE1zh<ń}:SVe*&iƷo[Rd $)۝_c}|OhcWa(h4Zr(SP\:{PIjUaLJѵ -Il5UGF.rb2G%Wb:،B<-~uun?(%diзs'MfiT.e8L>D˸gfLil"htJpjs~MirLm){_MK1OjDľOWa?tb :{uhonKԻh,H4>iqZgH<.;Kқ?Igz( u|v| >rZ)GJ]֖䅩s]I Q9+Sr[FaW!'0s=k2*^Ykoa!%兕HG8AwWTr|t.+L#9"zf ij9mvضm۶m۶5m۶m۶mv پՌ|@Tw/1_B▸93:x2b),ą/%ny釿_x HԜa>%:%u}P@6!t>tJZ!t8J4(Q :&&مOQ!53FFU8FAsTGtPu%YMFSq&&C5cHlzR+[(T>Zm}c*!H 6#8:~!CAOb4 ټ D̐/23 oeMXG)X10u:Cdԅ5Kee\{{+yTY+˸6H6|ۃ J-͖yW\Λ2ycr񈵡౷=AuxM=Z )ڋ7ũ3\rs''s 0b3sE3 D}FSZ~:eeɅ|r/NLs`⧈JyhSt/uV&2~}qtlqӬAW0lU#nnDD/.u4fhXۇt^mDZ(iduFIq|֊$0ۅt1s3Ӷ fŊ77j44`I.{U'2xc>/0G(N+=9҉Mv ' 1BfX-w{f|}0yz!/z 8Ep] w8;pp*]q$nՇ^؋ilvӣQh隴ŝ̖6 ! x"9tx;1+^m7%Pq w*"m,X/{mNvlB/pjB2ƉzPOxwQ]xD܎S6dy;", bYv8$d})Snd@EpV!ݐ<2iH8Z 9=<&ʊV$R'0OvؤàhP^+&02b[''V G$qO=-04F~2[;P3nDM zhI[U8qҫ{{}N<"fRcp^MT'yS[h8} ƟM")s\De6,Tp t5;߻ c5uYEDzVb.ΕJτm?j?ލOP,߆ :|7Ћvȼa9Ѹ}99V"9L ` NyؐͭL=hLפAxu:xv/8.2_}Mx"ܤx.p\~:PQ{_ tnjT3mR>ȏhPg`èj[eFK6rmڥ_!W^pihr6 ${6 W.*#S-=D 2Kr$ / ԫuFR/54[36ur7W6'#YMߦ"axh/-97eRLH~$%pOQDVgU++:|7V^Fyn'qmߘ;UP);,[΂>MKMS:9InT S^ BC>7h+8buр P|֜oy"yT㞑8aTZަ&jVf7ģ\Dܢtج4(gP،|{AN>G10[v_̺k#op>1II޼8^d_^R,dz)%??{Lr, SgMY=.o^ Z/#OsL,Y[i@i}` Z>˯.|Nc,-GM,]t[mDża}?*X10f# S}tIg\ ggG]EMCC2lAFWͿRL:3!V5B˒+iC`g [/^?SR y>3caF?i\H "!k-v:?ͯp_d[x^<)YeH5JV_6p:xIVdBۤoި.m ڃ𖾝5n;p&P MXUYH _޷8hqInݓ!:ȍ6U1WlMWUFpPq"º- Chc@EVWJkڵ5:Im$\60W40J]\?9glIv0 9yA?NL | #PZ5.q@xKj(R:\D~oAYaX7)s)ZvPXWmE<{9AkSJsJH>izv-2oȯǦp yAxz|;t>׃ex׫;ᨾ+E!lOou2\A9[>Ǵ${4Br[`O{Ѷ83*VUfEh{ja> g'p]r.92١|kT}^Nf%rmbhs)5 c|GFvwu|{ :B[HaEbsRcSsVSNlU-2IxM$[Va$d1D Ӆf_@ \\1](yh Af$y6/X̯"WYkϕ([!Ǥ=gz \ږ,5%#5c86QWU*'C ihF$\p[:p)^ףU$ Ԗ$#@Rn߅e =h/ܪ;>{~#\krI " 6l)̋8։/6|!:U:g0y=UPGT  Vg!5߶.$!)ʇf7zNr6)TͦdPH6:}`J/=^X66ߓ}r?T^L_0s}|Igw ˡp _ {4."ָK7aT\rai)i]9@9H<3LsC)=0 ڇN8N8k.(h_SRrΨle9F=qmIPٖyD-an,rWyzUy!dCϱXRzR3s删 " .WK)o!SBA [#ՙɃM(-q ;xQ}õZb&/>vPYO +(w%M5.*1@۳N\{_-p9rZ{Iy?Ff-9V_c_C3pdn˪IŤEcZ7Psn xcJm}krQ1݄TyŔ-9(Ḱ<{ >x&m|8&8+ jR5*ԧN &oV` L”4ŊEb'}_xo5-P! +DxdzAwf<7TI;mf3HGkEvt$ʸ5%DyU?Ξ6zeI!# z"хRDk{k8{fxkʾWԙZ6e -OIEIwh=yfO3;Ş4]F: ->eNY7S;jm QMP!x"qU=G x |>NLab`K"Ӓ{cARFCQL0c^ڦ;%lMrʖ(mzQmV؀1ʏ@mk۽kʩPiGv;jݖR^4MT 7)H6=g,ȉ,X)F9pBIcr)@/}?Lf&9C'nC,4 E|?|i쑷ЉuZS3!4r_ !MoG)mMqEtrimN[ߥL8kj|&E7IvdgC:18 0VݞSUzKl쁣[T#AN|!kJu ǃF>{S܊|=y_@c ,TAl,bF(] n€atx .AYk*"[7FyO~erMMiF'~I,(s9~mQNXܫk/޸j4%z?54%rY p(G3rc\P`^osءe<}Ֆ)?v¢{Gq;# E \γ53xݾ_&㵮ka 8#| ,XF4u E[}Ka~R#3k)v*۾(yfuDXGsgoi?6WT連csaO .0SQ=ˡ}wL4|8 C.vQaY"]Sk?deԐ穎BX JBSF9A `43tw7/i;:X(UdZ,ޛ`sy a#t2'o"l>@HkuN%"g?U/qHS@8w逩.~;"t4ۿo\A:Y)c=rP^A\s=60aSݧh1ٵKduJd.<31f>ڢ%9t@6MvWߊ6v? zsl}uǚhۃԒ,l"AFdnPzWx3T]+ yBV槀!= >6o86]N+p)N,}rLsCa65b Nu0$#֭#ʻ|#eggG7 DLOˏ :)iRw*,^ kc|y6\ktg@ewWlcSG$%?^tEг:RscJ>ô1(Sɉ W]TJp2teӴf|Ez6~jҼq<&agywGfZ&\CçrAE&bVI:ֵ#L4,aF4qv}}/F G J.(\'ʳ<(uب-LPQf%R(GE@fi 6-^U|?Za)_c/1NTԢ.AG|%5] pNې&M9u R_~\lR/֊|Qԛ;mFJtO =+KqEpY/Sᮝ@n:# )*vN2G`UeVw$7>. N6|іw; `? $4R@AgnHR y-[׺4>0%L|RG¬V"y}yRuWӻe-{4BӵFHLl6iʾm}$/ je$aINWس6x0*e}Gg/ KV).! Xϒ ^?7pQnͺ\o=5dwlbh qy09`:r|iC:[~3xU0VLJhGޱbKZ9y ⦜5]u/aSұ\^^ ȥI7l}CxaPBu\D؉ijolQɾo@=Oxh,Rp(uT &L4%BB%TPX#{7ly9<(!{`&RhADe Gv44cLovlj!E*Æ/^52TDR|KKG{jXmi5[Z|W=J1E2 Nzus)[xO:| fPr/u:[ox-<}+aTtIN^W'?T+xuq]1<ȒUiw*+8ON!SxuN nY=~F3J+v$=nm{k!u'wܝtЖiEm{5%E]Ltlb߿!ZM֌29IEGt.Ŵ ߾qþ OMſO۲PoPߧ#e~ =J~{=VrngvԐ~$|($'ՙr5a)nurE(ƻ%ivSgL;FW$dXWę:$7%s t;Րӗ&T"9*jN+HL #*/(7= "6V|s`i#KehXh3uA15-`@wғ8RrMEIFtZ2K+éZHCj\a>7duH Vh 'dCl2d2}̜lQx7-.1뛚&Z`V82m?eO"[X[:kDx ;yuYwM^YMK,ZtDkrP{nL'Hݝbw>5uߗ2k`h–v:lXZD%^t`=yaZeR,E'f5&fNY$`TIop,7-p@6jODkϚ?4zɃ [=k4rf.'4:<2V*F\lB-ܦS19iBo`Sڎx# @an<&,r4䂃hE"e#d= *3Ftp]`IDڕly2 V4Y'YD?4&ࢨ}s< 7[ y^QڌyAR,!T{Eupm`&3 tlVX܋WSJ|qZӗr0)Br R]h__!PBB&:գb$ z`A-ʆe1ITc{;#hBBu|:^7,lZ1vP8ɶgrpH̨:aL/d,E4EɮA?/}*ǎ?畃AkwVKRLFk2|>0ev:]>BJZDMײrȮ'W5^j~E`GJăYݏٿEBN^k+ s􎹰^΀:h,1RבY,g_9JSHuFk#R͠$^DfSͥWD B6+Pz5bۿt~(S9q6Ú>{i;Mt7 MYwMUEDZ[YtPjB/N }*t&Dե3$.u+ ޔoF4GY.3^֩E2S%b/J-GV4&^)"7R] Ps{E`:Fpr{,hU[NƦ˝_dOYU{ڗiW\xOS[RU~I07Jq^2b 9V&:cd-y"ڙHm`,9]eZ3zC Dt`GJڸ^F1;ƌzG9Q)͢HRp1.66\57ĩO3 A7!K~ljgko8vZ' O_Ib Sa':$.S5ww0XĽݜef%Pz*f_>l }s*mwJԜw±W9 :O:ց[@[ĉvyVsxױnlFEkN(LFĦtg\'x096x7 kAWajbwt34<$\"uέT;GH ՑW >>ÿ:~i޵xUl kIFTwזw<`mR[gߣ U'g㟁x2gG󸽵.{ɇs¥)VZ~"5iQ̷KA]ziN]BwE;d[M3M&ȠK=TNږ aWNn,2}ͲZ/hO륆|*-SP+nGˌQ)~IeC;˖<IN|i"+y"VKC4ySe<z{ TFK:E#C)ܻ]K>-7= =eP6F"hB-UBۢ晝^?E]kxWMҨ!8-I$puɾY!"Ht6d?[6/k3Z˜M0ֆr󨦇ތu4k+*aw #qH&%&sT0hgmHN(N[18[ËQ \+ x(S 6)p>F 匚ڂhD>Άj(B_ΥmN7rN%g÷n3 QZo aIL@zT7@ #}tx\ʲ\Z1PyϠ?\YfLm91x͎$yN =)G/2XmDtxC0+Z״CYC * @2E^ Ql9FS*e`B0(zփm"В [@L0xrˮu ].D rY) If)h01)9hc65:vȢT^h[UUCs)%!~CtUwXJ3H[٩B{zve  U]\8"@up.b#J %ʒt]TU$G 5k,S&0~, p܄B)vpİT\qIS1 `)Sc KǒL&J\ٗWWbw'* 3r79,CZr/?Z%dd#Y1Y9ƘˣN/٨ 9NSdɥFvu8G5'a|vK=._G!e  ZmH򄧼n 9h ElVT\ݨHzFJ(M|7̺';p$=f\|]URj}Vr**I4VV0W:/9f n"hW9YTC'j;30J/ GfsE:Y"`|(]}9=lV"}IdW$si~pɍ9҇,C֙H_bl]7"-meh[vt)]fcAӺ_ !^ރ5Yaﵢ z)A>G6?F ^ ¹I"iuYp=P;UX#ŘC\Gt>OgiAb *iT9D.jK[^~Oac:7Rl2UF=iͭ:sTcttUz!ikǧ N_x[~5ەx`L՘=yT !q bJsnN!3G+lE_E.()\h#\$x=򱂀/xx&pZ>_3^lX7J' )뚡bgɶ3FȜ#V+FًT"!O/>/,C;UF1(v@Zߛ-hc n4`wzT_^rU`#/}bV>H#Hr|p ѝ?qSp֦*ҡ.|6,s\hǦpOCy uBaN! K0 NŇ[aFTt-% ? PfuÔ hOS0gZ ;(Gp*I{ 9;<>ч E_1QR|ENgr"vJWV~sZ~O-jlh6rr (e}KzӢ ߬!7 Iwc-oͱ<6;|w̏ܩ ]Dro,FH翷O> *42@W)jb秜E)ISĒ[vm뫇jr5fa$+fQ ]Gz=,hɇ?qehq=r ek73W d8_{U+~TM / S܈E̠rx7Jr nࡵ[l5NԜ:NVT)\˝V3LV>?,5#N"Dჴ4p3!C:A6ZPd d Ϟ)2x|Rˮ˰ƌ(^do) zˌR8eO/q[Nh+Cذ)6P:g`q>r$GH 6EfĄx7?'p}.k;qG6L,t-Isn|l≮GţXAg 2lE㍹  SnSɭH RoIL_'8C·~A .JX@)Ae{L#0ٖ;"Zj2AT9XE秪b9`/ 9ZQVyu9ﭓǬR \+lPRsdU1 |.P,G;ۤ:t/% E{O'l+)|Iz(zPm(l z{ӑ]f-k 'g<͈V/ 2ܪӫ6 OZKX~@dQ :C⚊E*f )=s s+Ag/l*{msRiLG9TK_d6XX.VzFVDI h+fB֢cuHuh ek % "YݬEU9_:@J '&%M+%k*[.gT@_D@`Q|̜B)!#~'U/%{: Dn)xt}$8}bBznBH&"lD@aacH@-h-c0 ~bQo&S&|S'r^r:b$2)%txx5^"N| ` ] s 1])rqN,uRMP r>u>f1݇- C&mA6TǮZᅥb\׬@"Nh"8;>:3z1BF&͘'P*[j~8fZ{=A%^M8[L?WlR36IbU9W:nX4s=B NtHjIֳA=߆L\xwe]#ZcnCjmbY\)oS!yy [vWڲġx.&,i KkMoM78*=k"WĄ{Un POFQ۾]7䤗=e 3L.:#4&2SkȞJ]B5 dʊpbyl^I}*}ҽ3O!rX2.`nb'eg Lo%γek.3'a1,yVu?"!|I/QX@xU޿|%(KSCvL} -1t} q[:L:XgSGG{D{ɑ_\7,\37R)1XGdfb-[<)Cr/Vl^7n?0e"xЕL]jo 6B9 Dg-%jkaE6_]CUW"Rez':4cٓ]>\T>6͔uΜOjLeFpJY:/^/)-(n\uMNO؎7ks ptA;Z[Ց6LF[he[*xB֊_pEyH>jj"kLMOKßdm.1ɜp*&(BX2҇-O.Б %l@)1= &S_ؖHPK3J.!D@,2ǵr4ل(?E5,%`B%-qYVrYLt0ƞf[#BLrO+~XO2M PkozE^SB qoM 3s%)C;(XEm 믎w:ɓ>1!B^3z[T[3` ŰTԬEKuг%~&Aw_9P2q܆7_2L5oZ0<+Ex0oK [y;w`|%_(L! Z5=7abe7`-7H r}qZіTo? emk߅{%y}EI9,=S_uSl&f3"({R<<e@~ D>.`.sjHz7WS 3h2k̲ؓ<Yr8S*R|Iz0lVo(EmWhp‡qbkt</fz.;P. @^Ԋ8C1 lt>|\+njd|ʌ48Y #SA*+E+xuH E? Xm:%9vl1@IѲCo9_S7N:NΑ7=J7?Ņ]2a/pdEVE5BL5~edzL^!,9M|I!j͹`X%&#Ê|=)ft Dwo:_f.BBKbqw{VXةv2kߨ1Zc[A$N{eR26yf"s@;鞪Oc>܉Zz4RAQnn`>_m q$OPq5bhha&Jlx)2Aio `b[uondq %*z,3垸* C SM|mIC2tUnC6eAvh~Y{R ͺ.vkng';k0%rҝ0Iz<:? vs CV oԼNzw_H2Ǹ%筄Y/F04ySQ^De [#Zs}TxAlbWW s%"B q]Y`7iS^LEGpnA}ҡDaB2WLְ d3T,꿥i,|,JU 0bbד*Qϕ`]Sà߱mp' Sv3z;9~#0Hij`pVtt9ԭ>֤/,Q$يqWr 36'V9ەGI[LN8D< B*Kdbw'3w9}[`&rp"thm%h̶TF'oe`KUnpMPRRsȋ<Wœho$䶫uA|?'=KS}(Qn!ɖ?19N)#(p,r4ySI#Dfs(fKll |ov;mffȓ39mVw‹neڒpϕjת3m6<Z,j&$źᢏ٭]둝:/$NYGf<"vG"wuO4s)':nv0!Ӆg{Y/rLRBϗRԎ7&0SknN#|*JYڑmP2²^xՏ_m\ X8A;qQ`_8ޞl3bc[8u ]Pd[.+{=hOaO'<ИqcRh珁DڲFp+O4$pLjaa-坃|nMXl`!ɭL3 'Ϸac*|!JJlzrjN%$*ݾpN[!Br>_nk ?a q/P ΐӜ:P1[;,do g/FK-iK l@i_zl "k]9>'7phy^]f *^t?A9$V6 rt}[ܾ:tZio$?"Vqd fzf:aE!ũt9OQP˪Lt5nE-y Yyͅ7ۋPZ\MQ( Ns-n2=j,x4aE\{BoMr(mr \{}QݚRe 3yB`,75u`>bܷNv,kz Bnŝ\ 93 ״ӶGKAmJ,"M.bIdՔb:;̿.D Geimn=c}kLҌ´Y*Zq֭ 1,Pe:NdSZ]QAr.BuԲAևt鉵$vM}$ϖNV^~9n_5aN>%af@6}2c5jdsӃ$,9췸SB} ͋3ij\oBPV^gD?C nw U#PmJʓ9:E nAS[-XduǺ>V9q}Bj5C^|j?,,ײξsHNO ge ]3 zپ#P?U.:֟RKxUa)-+N*͊E aRiEV`nMsmX,1/ӸG׌g .}4*K1SzG/>yhtK[zFjUϟ/U68=LMuE[a07;뾓z:gEÊ{lA*_ k;Q/,Wپdd,QM %&ք0Yq?Q"+o6'Io6c;!VihޏB[T֋tH&6bB}%fN+ɘз7yD@k;%M B2⢰qB~KlK Dlvqlٱ8rcd^N CZ9-F=$p[XYH.11fT1b*O9|N +ydX|K&DGYMa;j.!WZJ>mhB i9L$U*,)ln L>8Ӝ]tFVn HA C6햏ca<G IjK7h7J@@KpћgoDZW9s*T'T§=lD/ w/J2(VIF 2ɪ Ͻ?$%\#qz}q^! cRHh!ކ~Y7F, X`tV}++gx 2/_lNcDa|3SqRAxoۅ@҂1EQ?!a1{`mDrL#Kw@g0& ՘Fn!x "5KQ~L"WzcB7bF>j(@$`wZNMP4h#I$גV _(! ~rپG. }1jzdVlZoǎ00. Rg=ஆ\$㗋:Dtk3M6wpc ƶ鉠ۮOFz4"$ODK Und/YJHdfefvY-SA5vnU}T.ȬTH2Rvb=dhiEh5(>WRHo|/ <&d91|h(\^qUH2p[I0䕶hLw|`8m2}MPty7}.u=5s.xd r+h=!рĬ1gIRE.,S*C#rҠUm5=3LkR}kv9+qc}.D~;#c->(Fު\>PxGZi@ UM\Ӈ5J#Wr6^+BU1;0>(aW<Nt-0 xFz퓜=qe X=b?6lּ~N|8IT MA%kDs:f H\͝" ِHxA%pOn/2zaJF&S?gXc!(i9 ^*akvt m`'o-5S]x)6yQ (\z.## vhBNԒB8(٫-("Z1gs?ZoXsO2+e+v4U6N{ m8W$FoveoRuI0F D,!fQ2}I< v؎aĤl6xxֹl(cA^ ;+9 \ ǐ#M:ߏ:wi~3?<Ä${9)EBlXU?fdҖ(ybPzغF~abN)>@E5"Ź֦5N{hea[A圏IwwzZ|=OGv X-e~z"uBݿ';ڨ%Lkc]d'ˠ|ϻ1!Ա Rwހaks7|XZ*u- jf6i摵L&'@r@t0 _3$UChX!M(1צ$XxbŤQoZ.~T.SY'< VZru?G/b=Y{& F+Dl|4x]<Ѱ2"ď*M͸ O3>*!٨r롆~He|$秠=*aĝEgñaO=Z!=%3⑈zzץ˝_ޙ&9*&9]A&^"*FsĴP#u?D6\G?kF "E4VRsWN.Uo\ky:@fxwÚM ܯw=6 dپ)s.21RgHns@OȪUL9އ9H8ޟvm4'Pɴ`sKblYn5MjZT)F@) w^)7]4(a佚d1aMMRT2H} ݜGYA(T _G+.4cc'psY"(8ޥTҙ48+W\V=7GHƅU49A#zogڴD # Z)~>*c=$p|fp=ǒ.qȣ9Ts#"K5wC\hi ,|^[Z[Ǫ [0ki:w: thٝV @lTv y],bA^?hfu}ـc=o~u,`>Q eqLl}x]Gڣ'2F'p/RD*l^(Y15oy Ee+'G?O GG:y5\!Ѻ5 /Rz8[ NWnQ* $QQSFjRl1 b;λoahз)S;4~(;*7 |~MePA @V}>4a)wpk>n whOGWhώenڭNPG rt/߯h_R{4l!7wAK?!Dl@̈́*Ȥ} 3BF+@ 8_Z?2Cj/&p@cTqE,[of <`s?ҕAH>Mb^ȦPL/wUl%B4&Qb,×+o͸\!\Ӻx\G @(nURO 3SI7"2E&jfۇ̈́nkdh Ls|kaP8w{&|EWn}l&ĪZ金{G@$(v4.dҙ)C=Lv"oZ`&2,CW@m1{IHfS #e3j3*Jʰ Xu.B6T`eH `0F2<S>حS2~KM$@xrQ:Yz! X YXz X 2߸|I7zLw5v)b e_~XHs"OPJ7uê"Y;+&`>{nǟjb3': S {E% g0N8` O k4sTRx:c#C.2PXبIK7 *܄B4)067Fqa)mD܂ %e`̕}s| %EdZb=v9[!EG=-!$( {,8ڍ%sR5uDcڝiǩ9W׻G[Nzr5hƼf7;+ӷR jҐqz Bf?b.DbŁ4ňG@W)PHT H*cocS:svkʕ*LVV umBT*~Ԝq%z3 ,@}:ngQh~4;T’DU[ x(܏L`AY4k0H})6 ]ڳ ̾&%J)_y^^1?0[%Q^M0C.֞dRHvqUp:9/'dyVی.}~\uts'o,h&/DFެ͜o;4mlapuW`RLz} pYG62ZY+G{;y058x$i2eÏ3!Y=>Rg cpWHCT\@]!DޏݥHJ2@w YvvtT~V2n_G"-HV5 n(_Byu%YPY8*M*QyXkڠZL-^p.`I$)DuxLWb%oBm P} }1.`[6JYJq< ݤU IzЋq`]̞#c_j-Ƽ,t^N2f-rǏK}+c_cR:D7*ܯ(DAMs6j\BƋlub -KzWrtx(T3Uf0fOj03wdUk:y Z@'KmEpԥf;LOIϽΫ2M=-KHON _-VĻFg q{W+Z ,Ze\m5V1)YCkt,@!o(ׁ;٦Abn>c P| BSv0V1Gxgl1)*w[P; 9#ODhlvt-X@NARIJ;7s1NJLLtZD$/eC/!tDDJ<(hkވP4~pg3圼6, pgj^|OVqk`ZX"vW->.L2Q9bui" 0kA?/+CՋb(RU֕^X+/{6 (S, pv,Sb27˒T)᠘!\ E,|cNj2'|mV^U}>5DXxKC}[Pk=X|rjMtz*wkA!1zLHHQ{d1ec>p%V..Kw/vgTՍ(?=ɡ{6N1w\7^JVVLq& $T!P|OTԮ@9j2홳L'_=>lʜa@Ig\|EPL)F_ :eP'usMzԒ*l!P{Q V^7eN1 9MAA Ƽ*wtzj;Υb=T$RRۇ33(PtvxfhN̒bNG%4*qLXc& O*t}%3>)'ݓʢ>10nR[1FlrqFiXЖD5?Qms(S*IK5eܵX܏hޫY[N ߰Nu endstream endobj 153 0 obj << /Length1 776 /Length2 61937 /Length3 0 /Length 62000 /Filter /FlateDecode >> stream x|S0ݲ-6m۶m۶mvm۶mvq12gYQ$J4v&v4 Jv.F&JNv4vPF&fPtؘӉȫ)QЌML5gr8:Y+?xvF&ɋooh`lbchogc+y898K998[;ZRr֘#+dghafO[FzzmmttnnniSPY8+88(?!j`caoi/qQC-D-M-M Lp6056KD_Ӱ>X&?][ؚK[[cGwXYWۖ1iNSrv2Q0GGFAA;w|/f6|fzz|FV|zF7rqt4uO/eZWezVsZȔzpטeùz $ԣ9M<`F 燔VGT|E_*\>.wsV7; pUz}jW.Y>ns}Q]2|4$^zb`@A1v΀= "H$"TORdץ|Sc0B͔}~UX_8pz922/ L%qB.5 iI`%АJMi֍L! ,ugQA W ;&_/@,|\ZEs 9߿/_ʆT쀩~Jr?m? BxW>{D@Ovw_a'\XO 꿷ɍpWDe[Ċg@'aOUķyh:J_^5 h>j{4slI~vß*cUrBٚOt@'(z)CB(hVjr.9Teo:faύЭE<o){ay"݉  }93w! \Ha K56{{g0IAъ&%<Φ cd:06-qg7b@gn+qP5WL,,^TV[9mllO[/3b9",&oF-.OhejrUc,jYvJ3Ov3ks$&4u?xBf w ;}TrfԵ{4-_ˣ;ch(4gMSA;_r&r+\}R6SD#,g:Wd; ~H1Z,}HE:󅅬ݓq= \1q:ڛ) . /9!~i]%w`̅j?26G dF} s39v _fIE r$D9gu# `dHG  bۙ mͧKEFb7.[0NsBjk1k1ZÃ%pɚXw=T`GVUv嫖^Jz6?(5)yd(Ćy2EDv n/:NJ#M8 Կioo^'a. & K ޞ5A_v W"Fֵ-0^ݜRwZA7bq"vgȇ.L&`rSS`9/09Z8lY@Rb@4vIiKPejKp,Od-Gq@ C;p@: =nDNѺ5^)yl^s0@ ,u;&ĕ8kAa0V*T)$*T:Fxxj>B( ~A96܊O!IyuA\A XXR,QYDǸ2̚$1bGDSTe`380锟^r/@}DEM )T\}+!Xv|w6mAC9UHڎ$Wmo V D\ ³.-LU o?X$,82U_md?7Ɉ-z!MD ‚D+j ~־J,F"`\q^AUZ8l,|Ђ;9(7K](6HƝY"":Uږpks*z"&*臥rJ+2KˆP^S?6z ͪ "•؅H^kh@|Q`bQyeL$tgd]ߧ]\&k(j6{hM$?gX0˧$\֕ "YCԚ@ey2=M8g֌-Q4+|p<Sfb:e`^\rHHX) 9ly_V8vSj<>[[|Gj5F'X%mOyn̴i](u}'=737DOT]R+.w3 Avg);$\Fb61E$J&w^,7¨_4S}7\om?!q8ˈ[-.װma5P_ mK$lD3&Q98Ԯd6xdg~ Mt%Aƌx(}Y)t#;Qb/o:vN/^!w=,!:j pw$pJREk-rh 6DI T NdP dAF]Rq9Xm ޖ Be [r@`kޝ({(7F̫Ke$G@UT8 Y3HPS֔%V N3|NsuEt^nu&4z25q5;a jmւT_Jܽ ngI#/ I4i~F3 Ǡq5x? G& hF+/6Bn: 8 1)W*q/;NR iUNU!PEێ)kl%~^:c؏BcΨȢ ~ +߄1%uKiAH&(GCiIT~r64K7;/~%<؄')7S$X~PXɳ|K z S*1Y+Ӭ)_yY3+| :}'zxc]8(j>@miD")b0p&kӬdݥzP N Qϥd^0pt@?V%)u2΂aqJpNAU!u72%j̺/SJP<B.V_! ]?4e'B+Ƞ^A5;fFyG?x}Zd}>W LԊHA ∈njX@=TSZӅ0mcw,m;;.߬_y 1l 9ttQ eoyp|TdaD$N%Cfqh~s=acRg.P2ʘQ=EBbixۄ󭂯*[f50RjyG'v7P b3eE=!@DLfekWsz^,6 @hCυ3wlhbˇrxOM5T=D0lmE9h'2jy p +W)o͆BSCb <:jt|^Kp[HE<=&#Uw+G0 D>q[zG@׳d<k;-HbźФ8մ$^ qaF5{D1wp1<:|>G0ȶyIӱy^d_CɒUxbZ ՗Pt%{`WS L+$Z]w>J~Q\%\7ۤ| +m02x;ґ?P;Z2_6Va1% F3|јj-1>oŴ'ƲRIa\A"<6,U6{DT;j%OCd1'L=5)2z}$?io 662Ӛu65J{7؅ܝN&S^Ę1rQ2jʮ?GMw|IVV6\2zm[do9iT+rv"nKef]~nue'i#k6s)MOH fkFӱ&M̋wYݠldK2^AĒ1"AI~6 ?>O̺3Lݠ_꧷jk}`M{5p;qĕ_ڬ SS<!i >.mE^u%E>#w;ޖT~TeŨΤJ_fj^A>na-GÛ9}Th7jP̯Nja4~ چJ';nAX*;wY;>U7ƴƙZ`K!`+!peZ_arO FݽWh}1H &kLeţƢFy:W*Aw)h̹{.aiuhₗ\>U{GRSGAY_<%T>r~$mĊN$ BPsO:mvP:"q-|**9Sn?E^az_WGЌKֶq5zԽycK^RS+'gE<]EopFh%k-ϏPL]_e%+3\ YMQyVڊY_GN.$l}*YJ¬ ļPdV^,}3(=/V-}aP#ylΜ^dO2{6%k)gBaq21<1[NlX)MVd= nZ `p.=1q,](,~=nY9i`8v_+TeTs`2ԪSaC]sޝRtPY{(u;%ʢu"Ʈ:-Pы ٽ '{S+HŽ)b 5#+:x&Z$=t2l5R>ʕ=Z)P_Pg67IF_t99uEѢ_#Lzu Qoi,Kмi _XK{V!CO/\Zm\/):tV2Cy*|=,9V5 #hd'py7*j{ bSWet"DԃN $6]UŽZrga'o4[8D^2J\:m: RC5qh~"au ~\Ԗ 78dm?䟆կtE5ec_7|$Eth "wKú|T7[~REx$6J @,Q]..IQ+^S7qoi^^N6of_k98R;2@N -1c!NZ-f8fL*[`#s/bZhhBu ݨqF&ꞁ ǧFL:Z#wvwq^+%&QЬcgW$t-' ~[V h3ae9+:uMXk%;u_[Xon< }'vutѴ2<9Y~iM=-G ،}'r@jd~-,cZ_[A Lhv[6QKXd2E;,Mafy?՘~~$](y'\B\I-ҀFw72;:P7!ےki[YEWHߡW^9GxfߛV)'38vc^~JٞUbb]7)Ӧ)%&G~|ذo0^`P&MmmݓKyEiċ⨫v'%Q(:YvN-=;bE]HV%byܑoO$V!A!rӬx.x>GBC?$nUkShN ;8Ť cDRu /)]9@wCgЧ1%<.ݦ0n y̤t>a7qPA__Ǔ)ÅA#[fH_FϟՖaJ-6sq̤E$u>TޘgVc߀_VCrO`,dKqg(jIGGLHEcu6t&n9k3Q Z6hvdTrcf=!}?+Q6ye F﹂/&~b%A@v6݃BēN$=<.LϷ]Y l ǚ׌OT {P5^iҾzrx1 ʋxGg.<["yU7v3>70kHd.ែ P4,{zhjoM"(;Ct捈־_W?]E,/ÀH(m d8w4{q~Nн*gl7Axޛ+1C``'; lǸtgپVŶ/ :Pdg7%ky?xzNcnEOR~^-4g{/B 4<#RƷE'-YoEF769;9 FmRH}}ӺPߪՄh ZvDuS S<>U ~]wYc|dx 敟J}[. g3q5f}le KJ`i[joZBe-oDhP憡8wse *AS /:F1=YK'M f j'sB&'mV P/9MDV}:q "#laŮ<7U/Ӊl.ZT!W};Ġ7{rQH oe2{ӡmH&'l;;U*z3tN̟-X<,/gP{üjQ:2TpM\?\د2yM'.H*cg21w(3*J$ |{rJ#d(Þ»Ģ#ML:|y+_ ^g}OEޠP! ɟ_hC ƌ(⩆&yg^{ BXǕjz7? bIM^F P;cB5sUSj6b W#Jl8Q-|'m,>HW[tnhu4[zb!5 _uSH*}E?1 C9vvE<)OXERt?m >0Nˬՙ#&ڄ[vArNGUQ;DEgVrαg$eVZ$`BPv!ylB^("Vdd sUiD.ka{Mhf,c@=Y/O&.$~z(WV~QpPJ$D]AS-@Isg`)YAͮ9LI L%'؁ICoWq$9g:'Hf@lh--xoIAt2[7>sv eG>] hua/~Bm+o$CK2O-\,]+`2CKu{[L)!ݤܓGxd(&bxuqBEgD+^dٙgatl[mBs.XOY $@'h~\ ucO[n.t’UיRܻ<Zp:I !du-]aUH&I#^ZIw0pwr3Ƙ`5}]"3Ν7WN~p ˙y[07 D?>! lsH'VG66 F#TGJBDGbߩq'\W.TZWNp 6 )ʪ앨VڕᘉihOI@VK16{p3 *i]H!$$! ,hlr+Uň[䃧9#Ci-דƚ-4k|ql >^.Sas.=CJ٤˺ҥ?u9zcd1M`TC[aSm)Q`hOykuȂȷʐzJnj+?C5@njcc>{ʀ6j]`qn0v=>#OV#QLBHҠ-ismG'2FljvϥЁ|9#Xg|sh=MA1/܏leם:Sx4u4`/&RehdnnfQuD ?[EQ ]R=*]fr؞/*=+1rcH#N5P:{7,9C?q㑌GP=30cljmKT9Fg/; !Ҋh]3"r ]a|Qmawt1- hx_6sV;΁[%rŘo`* ]SI *ɒ*$Y_ǯ;wMgH/sհ{hTë!lzL6]58÷!{srV4] J/p'-'3΀fULj.ӣ^mnIdF=({s"R I|6γo qBCYgɋD<-t?dM \褕mIQqW jvO̹7m Z' -4L%O%,kWfHJSK5R%U>A<Դh䄜?N*#4 Y zS՛ndF»芦k)&8bJ-0LF*ʽf m6Gd7ys|'9q,==`O<՘!yܼdʍHay3U_d@7[i&*y-PC.oAR~QE \{%TֻM8$ȟ`♩+fVmx<,NeC.ZWV|ӓT{\ 9#(V ,qR f0jךv!\rbνK-=Geܥ% Վ2!$ _<9#l\tT6["KjֱRc/"_Xxwϱ$+1*;jyҺ`wi=J:|!%:uFϡqg{;sXK^!&|^ }?Jr`K>nH /٬X{{@oY~q1rrR"#"!Nk_ INiܼۋ4Yt0!*!8H iD k[ܮ6s&,{[^ mgu " I 9r|crKum`wR9Qb տ KÈ֕ٲ݁w3 ׷@sue \k(* ܦR\ o[Q{QE~.72rf Htx`GD+XoH ٜӻO8%XxmI z0=ѷ2H`F9fQk<$Z^. ld)ܺ$-7N|'P>5"p}JVuC2Џ( eX l0f&P" Mۚ$tr 4Zd#N1̹PaA '8x,4L,csl k]7%!<)@7 w4_hB-)'xK|F4xMN~Nbe0'cF]cM8ۈ3g&sK! ؍B `oˑ ݷD9c!'UZW$cR0"k"~+@l;@;1&#ҫ́[Y)IDwl9/ UJ%(i_X+;M5N@(4Fvt\]1?#H*+'Goa骽(1lP:@?ډ[k~=y\I[:Nij8ϑt%Qb!o1 -D$sTQ#/Qdpm,+@7:smsP` : < y`MŖ0UMb#dp+)PEwP&(-?WJ b="'7 qձ׮S Γd5GN-d+"T&uc~Uٯ>I˹;ka8[Y>{}{=͜vyPkkS;^978a 2{Xq،"b݅ gtִ7/B=ݶC}٢,L| A]J/Γo(ԣ.1'';։~u^c9eU C,ގDD;*n_hDs)i$.!?+ (:F15}1qgh({m^zaVݧGx%C͔8jz@[Ԛ ' V,Y?x8=wdDOAgʘ+9P!"||˕EBЯJv'T_{_ԤJCgʦÎo#% qd[9j R9YVg=1@I,+i%S/8ɂ&>}nw3T_41UHU0!ѳ@q\CBYOį@f`r6($,{@sm 6i_.9 +fx(zbroB!at]/e):"@/Q}`&mF= S»%'W^:wqHߔ.[0q$]((ʔsj(_R0\D~OAYҥh>wrzjua~ ǍFjQ#/n ֋n51I*hU4)Kq.ެO)E033=C-M뜥Fļ~UY%MX`KZ"0، dŪo p~xt5S>rI*Q*jf r:%ۥm)Gt?#S }e$~2O`2gd=hM&zm;!3uJP(^=ev]@9 ?@;z_]l-xCI&("_~(T07ZRgQ1|)\%0y󐒈%$.C|̩R;;*SO>:f j!I}]o5]dQct| iK+;cI==E7>UeտwQsl&NF Wh_1jyOII, 4 gQ^ldbcuq1bX EsJDF[OE,*?lV !Jga=dz_6j3ʀg.;Q1@e'栔o 皝u.Ԅuja+c9M) CRDf34}Io'DrMmCJȔn͉-lā J?z\+?Qa@BjȦ&/nU,rR5lG+A{n;{IsFWV_ׂ1»Ul(7QYZ9]FƺoGnIN"ݽ;~ ->4`jܿ89֒MU5NCYk+Nt"\j[Qlfk$[sxyJ'S7 mJSCrYb5E@q:Tnty]P>:*0ÏP#r]gh&Mt3wcO*MZS2[ykTKRqx_j@PyuE"qy.0},ېtfKک}A {*ݛh&uQF2JT0zZ <5ٳ7eb Ojo;, Pih\R6bQ@c l=X.aOb%qu:)rЮF[,ٺW^s+_WyĄKXpq N%p^X?'dVr]^)#U䙫MٕdWW:}N(te_\%?nq"uVƽWm>䮚;,$ޗX笫*qOe1N5[eU/s ēbf9O[e+8~ש|+F{H](dpXQ7@r~\v~jwJ=NC8<([RǠQVwfs!1]EǯJGE}&iW x&-Y0zs7ZK !b*1u2BC İ\W*1 lԫofW3^К],YVK8SM'ݲE: UM]{K{z6r[Py&;[uٯ Q{MieG|GT΁{,!Zp`oG(8+ :g7M :GR8X*,e SwpDqk5h2YbuC8L=,rH*O"ZM|+5z&.ۂ4 ~PNJ\Zs_|m.#;0/51@Ee=!rHMLmJ$b/E;#6\={ޛ9Bф8=A^&㩔OoHgQozU'˷~?J-hùj # x:a1׾|YPTB尗CDawX=AR+rYȑfuiXQl= <g?w;h :n8p4mEN]BZ B?b!A.!PY B(fj v:a!<vY&GVw6)tW{`4Gie+ޒs`|WbT l3Ĺ\ݏj.U1ac^15[aE ǸP\>⑬\L;ӾM(SJnN+ߏ5iAk5׸q(=W5MYX۸$H}-.+b^|"i/6 wY"k5vޅЌZy*G L[BRh7S`mI`:aUsST?1_7=\|b,?$sg~tB IY"[#d HUuzSfn|I؜X%x`^eá3T^K=ǜdm/j0_۲]DR b#Ĥ8}{:R[ v'DnLa)8xGKy!%R_Ĩ@zNyyMw*&JZ 5{A2ؤ @[  1)w<ރ+[t q_4*l1i3L0ΧNV3[`D &bn}\ۏ V:r }&ɉL.~9)9VB_ߴ\`Č!')j[5V|YYYغi җ.k2 #/H{&ӀH`EjkV'mX2sQ eJ1(aXZ-طr$n*I0­tX$nEy[ Y\  E 9rSUf%RbHypt{Y[QrU/ATO=w,2` }T~]~}k&̶qmTʀOd-~n)%r5D|z Fi|Xny[Ǵ${dr 5':"es%whCqzH7:U@-L!h_p}r/$89."{g`oIfȼ\b ˧LH SH>Ef\P!Sc O#F=F]8تc_/BeSX|lzJ'U+E(X s-`75Q2Oilf!|4dmy  O< /H 5D{RᬫnR"/VN9Nc@?JSI3Cg> {,_ "Tw%i7'nl%v3[m7w;ƫkP;g}ѹ-Y/nNLO+1r75Q@')szt3!uؕ_yG#߁&b$IQz}s`^., P,Pe7bMCB9ţdm='wi  m^71I٤-lZ6 [4e%Vؖ(lV>WQ~?T?F-]%6_-'4"%`[RoFd7HTkGX"Wvy8QD?;x[nn5w#!,~Vze/6X;QdKҚry.{ o/F!vGQU@ZCQJ2UȰz+xa9}<244RV5}MƗN&AJcTaH76%E ܬXdbڑY5 ҐWQ *T' ۈ.OC]|_ARBL V#STC~o.@y5~ 7IɿmyDrUEsŕvaYt m [OX4BkHh|ݴYFwJ7sA#u5٩\XrFw;$x.n;> WAy Y[K1*NNyVliA%//-:B";VX`L1O/niْ3G 4 pZ$[/ ]^1VPJasNAw@ Q&.b ?Œ{XH]  =i',kXs*U72Y:y@`N5T7~c<*)b ;FbN{31]ADI'~W2ll 1Br/ǟSRdY{Gؒ/Rh_Jg(V) =MQO+-g%*-Wvr24&k&=S]Wvr_0l8xrJf!5ť BJvҁ&Ê{x'ijRU +PjƎh7O֋RDc\p>dORI9_yO,&]ا-Rw,v@؋NaፁA}><2tuAHjp%Ke?a0EQtFΗ{SlX(zg,9ڙMJ9Jl3v, 9+-pH)BMaR0!ǛdklDj?$9՞}p%x<:N*Țլ0GaFk ;J0I/ަNE ~gc³D:yCHv!j2cReD`Ck qĹ_|rȊG/Q[1/ X@ L*=O5{ǸJwdRo D*~]YeP[ylb3bOR>Om5$R\t'usxnrnYiɀZ,Gܢ;5$&BBRZxz_T=sDd_ mr#gpdDDH%bz[r(ckKO!W~HiX.J~zar(Y%/j,jIux@E f #Ӛ67Fs1MTp[-qiY3: Iyu${E2ZDŽac,q'k2,ea]],0@w̼!f 1x&GVx3תS1Ye"J<1 ?xp4Q ]pBA˖onCKN%F;df#7ߴB k`?mO}—c+ﶦ6Nakn')Rf"B;6I[}3ze&#I)-pE5M Tha w49hWX0'f0FrǂSGq߃ؠ<:;1x:+~H$*Uނtߏ Ea…zUk|ᒂL&~:nl2cUbB}#g,?l^ A'-v_F~sy]DW=7P)EzoXZM_3(>;ϓUFڤ;Ŝs 7L/JqXƄ:}`nkHڣ~OKp=)WX*;S),LR[,IH"e37ZVƚ{;Cw 4W mj2Jg*܏-(QW8rp k)|sftn4waEøЮ-.ylM?2h4Lџ?ֆ5.>|gA `%fq vB (B52|ZrNi,/p_G y,݀7Cʹ b#ZF䶋p9G/nH3P0 a* -T`ae נ'X]jYPGFea(f=}>yb _Kh%Iie`ΞIXmkL!*]~6q">2,uVEδ*ĉnBDşԮM7֘Y?Il i{dK oCCρ1U6V9RR4$F[ 1G[Pw2"/,Md7Дg# +Dv} T n|ީzybE]!ڈn}6[WP~ ˧wBq&/ǭǭPTJIB'lk gr>[Ojiz2p , `;U3I0|lqI{fk\e❨j*'Jn g3+faQ-ARV$o`gΧBkAAdoBe$^0'D[l|Z V: PӅ|}Yؼ;L%fE9U*xw#.:a_77k1VТeT9n.-Vk>AS;G.,r>e'j6O=FϥAC!zyuzM xZlQJS1 8.QԈ2k6GB_nL,ߒDo7d] M@`l۵3R;w)2'p }<2m6YZVU;Ox9vsGJ7Z &|*'{ʄ˟/whS e%hFؐ>nqbku u|N#ͰjVe¾~Qוiic; $ydtSܕ 8~yOu)V&F,|dT Z i-Cg^`)"á0TH# ӉUL$!~E+d8(('GzN1) zn2g\\ X`VGgqle^&##/7K+CBM|>IÜL.vܑK:v"r4VlYUTtd3u`,^ .}%.$#gA.k6(%0 P-<$䕝بĵ3#I4(#ATIt*Wq%?yq.9.=]a%MɆACӣaMӏGޑ>$a_<Nta~up ݽWKyhF$[ TZՐ f닡ۋ(Ut  `TRT=MWac&4=<ɿgmvt4΍YWfFxGLYՁACcYTS\ v'eA5.a zHkdqik^X1W#VnW¸0S\~X+)A+Vw ǥ|)ۉ]wMseE#ֽRr9IAX4k:@4q u8»UdV XFӭt.e qX1v/\CK5YS<@$T8;8K)7wGY_ᶃ:aex Kʐخkc,2(8|S^ a z!/z 8ESqJR 2Xsm]zfQA(yC荴ߞОGpb7Cf~ Q}t^iESۘ`韾fuϗ4E$ql5LI}{/gj6zN17*4J\Q {/ms-;m `B=odF+Vj  l@|=SO- ={Trxr奌ܰKAʌ{HՇǧ')-VAf锦C% -bC:xhirS'܁HE#{ǔQ>Lhw̞t&ry\BAj}[#ss`ypЬSM"ͺ2‡F>!E|_ ;VHrVsرx,"ֺrNXF)9M|3)f 0OYz>oEr}KCHjHqA4;|Aߢ˰:e]nmNW9 8v{Kf_EH+~##uS׃kͬuEhaDu}S%TeGpc{<.b.qN 1Â<s(3lX= hnIx' w$.V_yyW39fD3'/JBWKr&nFh:+q=aram=%X/`qc v/WOZTG/q U̽+h`FǖlN)( *j;bp a2d"cTEL1,TwT'`epg:8)<&B#ba0~.aШG_ٶţm20!nu| 0b611yJ KQTP`n&_Lz@ Ol'X͓O'*_`Ք/ MZ?n&/}r? Z6vUtvhO|p)E:AT?X0<U顊ZY~Moj0 b彙b,&}\,xL.v"\8lћfdWՇWOxm &1uN0pUٛs4'svRomп@Z3N@̃ N!h邏OԸJUT 5BV `~b6 prKfcg/L( 2ZyxE.?EO3bvu$WǨ9.Rxe`XԆ=bBv2+(R~:[xw'|`j[雃 pow$y߻H~z-OyDV~F@䙞H >zp$f䘛_l6 uֳBk;#Zfٰ&x2 PW8?gLU3k)/1&N}>}<;yis" ߵm <*j D"v.]sܸvvEc3@g_ Z+%oZ/ui)JhܚLɧ&6i ~dڌx=$9,[>.-!u(-1b1D kpb0i⻽n+ϫȍ +7c+XA&\=RbtX6@`Ѡ.*+x`1A(-;k7k1.}71)K,+ ~sBJ?l!P&\s/Xn'ћɋ3:+~&3fQ\sugl (v 69 Hq%crϴV:WYщˁ2m LowwAH3-W=DeCe!2,DbRWQQ8 :Hl+WV aM%( tK}HXJJ PAz xw=3`;@4&@6WԘD<فMCXcIY_nVL!oZ.&gUɠ.ƔR}|K( ?EdܼoLaB.PYn1p?h'u UD n;" fEjzBbb :h PMNDǙ=UT w)*ۓluW-/LOI"xv-;:&!!R>m Z^ԂݚU*+'ʗmHf(0&Hz}=}lmeN!(Z^5ri- ĕZ͌$O6'^X ablc;/f+;] v qsd<\[.>s4a1ԚPfb< PCRKr2CsUU<&Y(~& 3&/ ^']8bVGpg0CTCE;  I,0iJx`8g񂢉9ؗ_iR2]A$v%O~cD FS=R+ls@'KT.]n w,K~P1zȂqtȦMH9h,``Nidh͐![+Gv hZ9%w' -~-wvf^e߮-cnRv=T7 %70~zVFDi340c%/ۍ8"UhX%6o֯zżf{vM 7VCfß~(dc"OQkW7LqtJHQ :rf` ;Ù9OeՔ-Q ʛRi,02voOHOGW%"U̾?C?7 hR;3n\1ݥm;*0toG|~Q/f)(h1W{vʬJ+67rMʦt;Vwden6h㜝ڸ8#5F]` pHbڢR0 QV/!u q^0W}/h ߗ* .a99bQhiP=aPF2` #;4Zaߕ|;oyg^8lan)ˎCmcQ+2w_C}P\0V~-\]ru̝$"c?c ޹7Z XzQv āݝl1*8?jdr^^ޙ=J0V[O$>Rԃ7NE)JȶQJ=xfρ5@a#ʫd{(=E7`TS9.xmNtݤraG/T;FRwSUf5osTj_dȴC?:&#䡾mڭ2D@$vsaXl ~~LL(_y7UVW_L=Zӓi6v0qBw`x" T8QZf2 ]ܢQWӎ*V@PXGP: G0%m*Y ⾙ShR,9$;B`#ybb'Sel=hi`,u: l؋ qi i6G soPff |J8k["9oYZCҸhpTb4vué(M9EMꎚHgw) HNo &Q+S-i`aͼǩ03 pjVO0n߃51 {9~ZEG9{BWp9-^+r$ARAuu0DN{3ejx^-Vq fs$H m ڰX)|9Per 7Gwzuvo#x;Ŕk.÷b4cS> ^ 9uiŚG4Luџt]Vlb``gQ2z1@!=Og˅ZرpJDme|7jM3EԻxKs8Vp[ߵOLBP`ǐf2>KSy_{S:onH]O "hEJB|DlfʐAvW͐(5dtܔ^0/ew#0|X(<k؍vqőM-'UoHq>d|A3t~LӑXxf׃7=pp4#ԣYfatR..끏R; ='};a|屈П]7~iGơdI E" HFrϰi17}DM xt}mi8}Ī6 De-HIAF^rj[O_:$B=xG_5̞;O@r$6F@ /Ru cJ/wT- li<\\{e<4>bDK2_1nj{'9{wfx[ZD=P*v#+qf${1 wF94QJc48`eYhC(d\wY!l9{iuil׏FX -G-e\6,{mE|w`P4(OT\ W'FBmnl 4>'E଀]oSIw?̞Ord_vIE(&>)TQ(4@Orz ͚-~>ZujR%3쵪@BVvf}R/aύDMM9ankڿ&@hFT>[!L4d ,H@Rw۩!47 ߋ,8`W]%^Z!}t)nb2]@|Tt'f&d#ǕaZP%y݃X+0k]"=Kf C ZYZ1ѻe s$YtlMw42Ն{?=tt|A9xJC9cq"~/]lN Գw^7A*S G.ɊiKD.ǧ7*icK E !YGp )fA'W9Mu&tkRþwwc0D 2AFXq7?MlŲOm+f\̛:<|ST- _XJ܂Zxu|N*:H׈ǵ,ə+oz`|CJ\}7I, qx;egZCg3<NDwr|T??_?o[fi͞S%,~ ~dxkQWLLO逧cK/.;Օ#ؽi=QV-q8%m})VnA7Ѕ=!c15MސLOI@rICeoϩa7x8 r׎:EgFF~7洠#<66۾<V&[B=NU2qLQ9 ^Y{ \D\-)?H?4|Ԇ=or#{CV)ԯq',ЮRJqW>|ļpy<>*]cV8æI6%ZxRbKa}  ԐVQ=KΕ9̦gP0Jݘ/>Gk)`ѶWQxꀊsIҺa鯰rs#Ɗt6ɡwY£u7=t&gn^W43oOX_d.au"Oװw#ꏳYQI >˘F ,5[X ܦ!+)5?T)XWADao{t kH95+Ya PmF "\c58Nv raYJbuÓ)=zP@z ҡLs=x A{/~7|1t\cJbsbEzIniDœ `>۟יI +xTbG|ö-_7w9B# ,w`GXOYBe~;M=!-/-6 uHX.Hiu6_m2b`: rsˬٳ򞯆4&*ƾY%DIp1>]cίosC|;Qwuݽc~f?ܜ6| }ESmQ3_͵>[41|EBV<02NS[vw:Îaib6 &vzoAYo@&Nk!NNV(=r=(IdosoWu:#1ξ,̳̿_gW/a.0Q:5P'Ϲ`x\|3m势|/ӳ(H$:Mi<MXdlH56جN u"^SGgxBp591X{5ȁ5M۫%_C2?ԝ y{Õhs@/W`B^ؗ;2e-%6>$P{@BލH] f)p+G(ЖjZѵOՎE@x >ĸ-FӵZ*a\A }!Rc J4 r/+qFpԜ_(ף֪' &Y3_HWycvTң^+,ИetR=kU0 oHC Ǔ3 j\P| wvԬ|n4A0tBgiPuF(g|2Bmޠ"\zA ThQs&c||k5x-8sBJ{'}w,6$To̯nk޻Al_F( k2!2Ok( R"/^ZSɀ s+\`᠁qeQ|ތ@3fC1]k1V~Gsxc/d$`{qT"n5c>l6YC ssK$10ZOMkX͉XU Fg'?:# G#J@vcI]~rHs""*=Ö".2d}EbQä=+=i[[=sRRu=^,̍@i9lD+Pl4FMFChi5mLow)u/EF{*,yK^{A`}%jaP67/Q٩Dmzuj?򴘆 {02/H2(.8HoL }1AhoAZdE(?p6G ^gG j!maRIݙ UzGe{sogU;ºk^:LRr$"JT25)Ibb̳N"/W4=7J ad:5.w35R%;m(V\8.k}Bf_OFZ&p\hxAӑS"2=xl9S(QmͅzMN~:ݕ4L޾@[] G)  ̈6??Qy;?ĻH*j!ūM1L°A>hZ3'sʍZT^E ih?|.aB`Su7vJ~})>=FҌ/q֖5z OOk`\^3WR}Qy zs䄱TSbã i8#O4{~Z}DHBm"T  gݒ YdV7&aET^* ٚϴ h6 0J,hh" :< *@jby`~I_h޳f.3~?1Se|l0)YygD. zo9/ (QIh.WH/sZLնH&/+?00Ii8[m蹘C<7=HDh$9&n=ik=oANnj~4\Oѻu`6G2C##Ôa[K:aY= >. ~XJ͙\[CW3\ 蚝MLJ:~v^)Q F89D_`?!fԊ93v#r5xk,l9ܥ";4㭂Aߗ</˜܂hakjI ']Q\wp+c ]Ez*RʔW0OJ-xR.ߧEޱprSTze@-P$Xh&ųfH7_IKkLyg TrU_i8}Mպzϋ<˜*TWjȟ#[d?H].)X)UrMTA Ukp*Y !w<1 ioh4QqoB"D~w#Ǭ`E3f2wcOmV}΂<=hOIZG9PU:w^TMئ?'N`Q .S,EAT3whl?1wRދTtU9 f`)ƅn*ٸ,ߥEY މ2I9lY8w_VWr!}+d{g$(NC"Z:2n?[j_Tc!)Y(|@`J!7 V O GATGJ?H_8~8hK|B%;/2!ɹ2OgU?[ $hWB^dܽ:?x 2BOqphW,eVp:5vLE,k}&1 0BЎKQP{M"GIC"~j4BaU`aH mYE64R*#"ƑD+)`?2;ux^>/iVc8'xoNd y"M:Xe/Ԫ +9;rpzm,pKP+ III=ōRA}A 2x:&n}:H)[*+.>ZV}oF5GooҎ}p}1>>FqorRL - yjxf5M Bf"hDnK!1JnH"uզj8OEO@Ґ.)UK <ڸgI vv*&Z^ɚPwJҒH*.d7}8Y.B^TNoj^n\!ƝƜ|d+1!DSj!fъ/0ŒXq`\V]EKH [ñpKR뢎d&2F5C~J°UIb; :A~,'?2 OEw4ULxG$o :p< t#u>1& Bj _rX}[3W~et}54ǯ7!(ĜvTV}ǞP)8U4 XLݚW<ȽQ ՈH҄El.QR (Lԑ|m::U秖U.d.ںOD.0RwE Dn+ti j2QlK` uR׬,Xֶ)ıxl30X_Cqc2M$qTI/6za1 #J>VoJ)7';*O?)ij[ qH?-3Apx`&ч+5'daC/g ;#CXjP;\?ip[݆=L!s4^+c sϥ@KQ9;EK2>,DK !EP(9Ŏ:ᨑ l=Ll ER~.L迭0V,"3{+m5oZmEkN$,[Lu3G`t*٧ "'ܨJE CF HrI& d>{̈́e<4z.BP(*)֛Qpιכ zpRŹEA0?~~zUEэҚ8!QJ"DfFIYa`Ijl`F{/5rۏcIn7sXfoct'; %U&r#'40RE<¼w>?GDFwt[i_FMePÍM71~v08(V'ʕ!C()SH԰FϘUt;]s oeZ](WW/DO)n[? 5[ǾHJx֓aFܖ#Bwu@1n>k{+#yXHXk##l֏Ϗ`jtFo!>1d?(;ac8?;i;0+{4ů"t`{3 ydٻ O wyo_* v@a|5n_-LFGRʠ\]5Lۉ ːu*Ls9ݟk4(c]wĦpt5auer>sMGh.VaW ,u:;`05LTZt mh1Gڿ">[<~n 3)-,7JJZ &ynJuÿ)Sܵ4N+5n[gZm^:wXigBZUha`ޤ^=PAJK:TvLXpwzˈ$kssa0_ (ѩf@Cp_5U-)ݡ$d\Y̹)򗢁sc&.A*B` ɫ`U%VYRHN̦ǔ">h*׏duyV[Vf!'@Y@SZܺ])ޣyc%H˗.Ѝ9/c6aCYڶig`>d\=(ZiGgbwpY;(xUx9ꖂ#v_GEp~;uw-GnQkrشTe8!\IRix4y7H)xnlD#X{/(׼uhY,G _(L)8釐ӕa`vTlrJNר]& pߙața+d@' Ҡtԧ4+@lC8WA2'ad`H#=<2B1ۜMM:ϡ^|Փzc#?d׺r_DrDY&rygwKYZuG7f 4ÇP%M8?G؅ |!SoZD}7nѕ]¹nkĖ5#;ln HiRh|x)Y-m6tiOe38zG&йmo'Á(C&P6gUdѯw_bsi%%QZ%s+Vm({boOv(u}Iʼn۫]eYTkPh@9ٽ ܐaf>YX?.ePcs\ݥA9xnځѥtPaEkP/,RvcOq01MQ"OX޶O烒n_\U"NӁܦg7fOr|lng\ \ΚeO s؁PtS0K4b^2[5:)pWhqx.00vkPןqJO:Ug2t[0@4EJ>D~ ! T5Bol?8ykPT\CQM1{S-'ND7;3C7*ϒMd¿dwH%j*qCCHFS`. 8=}$V$geS&"YpFt[9nɸ^+{xGq+~8! [o96F4B)z ڴvX#E ! =&kt2hg%%i뿓c8Pn;:}Y6΍{˓m!1wlU{ˆ9N g`h'J5>6[. g?u7UV'V.<?&XYamN1o|ۦESeLasSGH_&suOufH uWsw54ozfZ]C[[I9R.Fkv 19P3ULɌ^F;7 G=X|D#}&"k4DwE=h tsz'fC kg)WC]+ץ_|2h{:Qov<7|aE/ĥA&a:SAq%!\'o"P8 `7-k@0[' i~rpFStMPF%Z0#ޮe ^ߐ#hiRD·b~g!mvH8*U¦ٜo6Y >2 DA抆J(ϑ8g)|7 |3"҆Gݨͺ:|yO/zsx53S9`1%Ԧ=L6"o .d'a`7_ռ&[hVA)/Yn٣Fgz&{ =k7#65蔮|j(AI]+H~kIOQqRF 9@|R>'.`G5^8S_ W] uuāX9A4|gQj_DM.D.YHvɉȫߠm1d[99u5"࿬䎻׹MCi0ӳz\.i ܈|R2{حYpmRFn;FI)&R|T!B| ǻ/A2, [.ŧ^n҇cxT㢴wX7x%L7l%,9+k[pЊ+N;yRO`XWtĪh0V?`+gpCw"[sL W-'SjlhH|~daV7"l~誼x Eh󒇾`:# +k3 L;Mba@w9})02eJ(yT-Ic yGRMyT* uync.`> qɆ78<YN) e߅L0}[05X).d]})ľ+h-M& hk2P~XQ퀶 (Ԯn w@?BQF^L޶{V_cMG'*Iε <5سc^xS XjA=ԑv RN_xo//__Un1[Ub r>[R?Gj: >D ]QQ5Gi-xA aԽ]o`[W&&r}}Ӵ-{d ٥eڣ 9r^A sݏx:V,Ԫ^/N\{T;rtH|(>J:&U`L';Cb!DQBGÖdE rse k;*(b[q݌9lMS:>P'!&ͨYܛPyLm]y&h/ ^c.]syS'rI,r4*둦\ݞ DIw'⹎b2ӗGv_$|RFۍ*+ey ]SM=#Mu)jc qt3oyLYww#i2DmKH>']3|ny W ezUDM8OQ4K2Q|T0fu7˅go(niĒS5=>%G f*dmGj FXfR>{%}iTD/pԵ>12!ƿM:M@p2cU& ̢+q>ћ]<ʖ^"~]&J|Xsڍ:5WP2ǘs942"5"=(5'QbmV4IP:\Vblr# YbUb#yGDm~}1h)CIt3%YDri>EO<6-BfP- l[¼^@-jO}c h_`Ċ}<^nk1\[KdxЊZ8"z-fzTEfo~\y'kO#\١7'};'omCR\NJ +wb\vG9Э -Ygԋ<_*KA5aם [7Ij 沖;!'/nh3}$(CGY8 2Abk*aF{q` 'srz"ɬp4uX٫Wuf09hLOn..+6#-p^F'$||5fx\Ƽ\2[!!i/c*n;7Y91$Vz;sYuI&4eڞʼ֎pVAMpb/[H̯"_vH [N|pڸ6R3b(te%8(C!3*L{?teJ/Rтf*(XYMɀk aǖ }RCl7c JEsܽpЮ9үW1d"`+%߄ y~ MW=Oܠ]d^x`7-2'+!5Jt>Ϧ"7kt~ݾX[N71 Fs5˧=RC]t)AnHŞi$={2"i"r.WHH$Fk}{_0^ce oYW ï+xvaSG=GV/Sa2m@'C|m$#m] gaԩl &WQpZVҵc3 ]ZێRx:T,'n~r.c~ '$&VΗP 81ґ̈́>Jc x:4*LduvQQTң,2at4}ln&;<+俏~jlOpLq%Ϧ'acHIz5xdEYc6ck4k sGt1'ReM7oqa =$E_j|ټc<Q=/Qdc\j,Rx# +$󑺦vdh +A=svk:K8~W"}nUkb;{IR*]sq0(sFp;623&Z#Bs#`rG.D̰ xڪc}#;i\bsI@C\&tgV`) dѶ.*4{ v/ KJ[@$GI-UVnHEu{hmUa cpޕ{!y|j'2B +p%+墦Ӱj0u(<MFeM]qZvFs;ӥ\_n5}4UuCùN; 2o4;|!h82 ۡ3w/ oeHh𔁎˽u9uvѩ]P}HԢMIɯ?Q4)Xw1ǪR[ |`$ ^3+B{ӗ* pt4J}l"߭9R2WL Oh-t, ve6HOŚ<;u R2&biE8o0X Oq|O@)Btt,"ˬGT%p$ #3\鋞Rx`(\Ic?657q.@p¸ f3Q&)#BRf31뤗儋`zĭ!Gp1ΰ}ʓRiTW%a\-,[ZΆYݰM03,zd {1B,NySК7Ua!ǜ  \&T0oިkoR E#aS{l^2ѐ`je"n=쏏˽fV 7Xa ņ'T 'BAs6a((H(adYpdgI:\Z*M8-NqK \*rr͓:,hg<3{[ DBނ!1ju8KM6R]cVeOH1MLۤz%@zc ;G+fphk4ޟ_z2P1pd'7OA&8 *q&-2gN! `< ϽkM(HP 0ם2@@y;(q gVAq}t$0C;{wG&\7H2r͸ S $14&8_e pg1e{(~5;|y0IZ0-aPNWdi5u+tHU,v-pbfMR (QXDeI.KpVUo!];z@\L=B'B񪺮81zpVt$s0aѹW}EP`'COzFxT‚,gn[Dp(:_!ѣ9٩jx 1bU0/S *wjU&cHQ{+;~p"W 9nè%|}a&8x"J(՘Lج-+puQ[$", yo};_JcپuR㈲c[ ca2 ZP % Zk;6$*AuP]FWI#x̨%%k2{VfLݟ*toy`dO ́#+-=LmS\Ϩ],`v8Ϋ0򛺩%"Ã?!Lq .G_GI0}StX(=" _䢼(P~:ut&H8MnI' pް4ԋlr"͙7i&54T+ot 9F>BYDXfqwMC4yy9pH:}ZXݖk#WW4ZD,;1ư0[i=05I>ԣ];O}l~ itRȬ Fvà4Hg\b< Hu[Npb.$]Ҡs)3fbW,^ ?+m+?dFc8O[AHȄ+ێ+ΰ= ظ5YQ-ghS}@p5^rf0pؖ.9[*Pf-1Bsv/t ^=a{1^H>`R-n\l;1]hxf^:"& OġzjUmx*w(\#vc?x?ܱ%{4u7+6мޠ$cLB;txwr%p-< >brPoeF#3{8Dg(me|3pIe>%F C NTɀ'We'#]"?S(¶&qa iaa9"nsABbگ9ZspEB7qm+YF a|he] \ (;Hڧ䄁̜=äSV  2C>m`! 8{(i`QZt^ڄ|[f! ژ3X" 3;2z`26 dr89}~^=QlC!q<=\"\vEw+ƄVO8/ZcWen{3h<*VGO\MO.dGL]IERx`:pf<]hL9t rH.,)mfWaƖ[^fyyLG#`TFݸƹV%k8!X}yܙ` 1Hpf3 H,b]_ɖw2 cfE}1h;.6k~%|(dQ«&4AN!A7ԸBE֓sk62n#W;7x>0+g?eu^ewȎ"#BpEkA n\Z`sA5ˍ-w5;1@ζ6; yDӞGmMz3j9BCPp6+c`Gp;5̧- Ű I]>Aœnڿh8*\|KW'%`8z+u_n Ӕ.)U$Ⱦ+߉kvYXћh/aO ?(j>_Ps|''3 ~ģt~z1Wm2ʈأKyu~zvkj 7@,43eOວSm|n#&WLi2,=M#{;C[,;*/3 D@&\y/?/-ޚ_Th v,u艡mF2~ tZT.mL݃gC1گ{[ x_N ]: ~Zމ RT6*/﹬zbv&߱ zwfG-}{n_[/7R[ʺ!XKb˱׼/$AbjRtvzw1Ugt8wЌxd#0[ihklUGg}u*H|m$ 0moO8,}?qjdA. 2"}A(Q3L+\}b%ta'a v]c=%['bW#'Z id @o^7th@_ceg{價ԟG^*ҪJ$[aĶq0s&-]`?r@kUФ| y)T憫{ 4"D-Ox7-Q,/{m;zT0_y,^{AlcW?ft" 2U hx[+Suobɪ^%w2y "R.r3ga:"9 =ׁ(͊\iY O68Qh?ՒXڊRЇ7 u*YaI SZPp|͘:In=cI!-鿎N3 7Ɗ@G/nD6Z}B^`Q1Sp`𩘉!eLSd+D=㓕{6-=ע`R'dqZo-]ah \/>&ArOTO;`r2;aMLJTE9W8$ 9M/3!]~4Is$h@hŰUwI"]]8 2gnZ~074,4hݐV鬍u>1T}r<\E~;#TtauH̺u3^'6exDw,Xl $m~u'ҥ lh ۪1ڴX* %|/xIOznؑgAn*[UɩCR WAېt_AGgoE* KaA:sFzW۲NOM:R" vz$3l KiÐ%/(4"WTXQ`S@?!a!#ɓ2A*,?.$hL`Ast#?ѧ%o(Jg/E2rD聅/dg3b!E{ۯBFVNF |`e Ĥ}YBn#.l?RB,;jk߈lRE\.$<`Pڃ r=iO~G~*ubD@R`E ..:E5K/IFz%&[̚Lr@Б5IYĦ 4:f-Y9e*NѼnB95o:a{pwٶ(S gCq-H+,A̾];of1\ ,4Kwuzzӈ. BR\;C|PnZ w]2=ءoN{!6 Djzƽ!w R9Hvg(v&CN.z01W9]9d7vBiGF{xtI<-[\De5Q}= rSg5s>ێBL *(^0ݹcF%'=U)N$ro{j~TvZ{m D7 ͝1("& pƈTGיw(n$d|_"-}xRC嫟)œ%2Lզ0!aE_w"k&ШPGI~. [zň8֊$ѦZnS"KpZ?5(~+ A56 [,-AIRvڡm4 4x߮W0lV.in\4$>S`jZն]G=,շ M]"{ɫ~U+IW%<,5w:CA{mVOʈV( E".=aM\=@ҍwy oDͨ*evSDSfΕ"oS>~Cjmn  D#![馹eW4.ZfCH1Lo̫H\Ϋ4rwZX?#^j:zWe@!RaP:Ųxn S:=tq ?pJ4IRe l2uo2‘\Y/&g#(ڋ:b=y8wrg6,~ݬBe\~yY|n(»X3~Bs3ת_N  H$&qG@ع(It~+&jzfݗ[uj^_i3o쯧=I/r\LlqQ84{E36_֧ @"Q @P;UN{Ň>Q[9~ e(mOb X#;0oZ&d |`-TxD=Xl/H3l|UYSӜ5QWlUg}V= مHfbSpPGsҬC/ A`!s A-1rWhU¡ 5!ya"$i' ц3r1Wm64X7hr>lMn l BU4`}KGa:2!FܻXe Կ;mRg:_ /O bb#^d5Q TO)X"&BJ2Dc\oBʤ,yGY)*i^!+;&$QeRiuvÏo2nvgՁ4xoTV |ЯL_.NN zeq}Ya pgXasJI] `v:H\]k0Ll;I#Ϳ4[Bi2v$u(㸳N=ϏviME.9•|wgick8l6΀wzX<<̴0f5b"FaWmH@\]z+DkBFr^E7mR/zsn}-e1AkMRȢ1ֳg" d,f#PէƵWY#zBOݽP|Ԑ B߮ԝm{xK&%R=\VX@&[q.h^vGZ|R~yEM\[]I:؍f ˔Vr=;N@`G/ + V,89]UI#5> stream x|c.Ͳ-lo۶ٶm۶mۘmm۶Ϸ:}GeD1GT)3-#@DIюVD 00rY"k`cWTf+ikjOc jdag W JxvF&X8 Ύ&6V;SPpr6qH998,l&[YJ89qٹ:;Z8ϼvf;db`dW͍ٞ_8:#;zmJ9@D[AGZǿ1#Z8Y[8L Lt60560ǥbklhmak"od/iYǔ?,[Q-lq{ZYW-csHSrv2Q0GGD!!;w- +  `d``|#GG[?~_?-q[ge8Qo#ev w8R2]&'y0}*!s|ّ!r`;>nƒPM/fpT@f-p 9|ʠO@Ѕ>'c/{wvZO^u5{ˠ;Z[l 2(PD<,8 xgtèSӚ](̦wʬIM9cEXEfQĸC㹪fA4 4-\ CIwyeAzs/f|+:Vr[f$u;|< Hx9uKDT)5[5moF.m΁}ml¸\B L+QXH6QѶBDH,(zWn|Gq5yg$lP8Yځr~7/ȞLp%=IrhY|c|WojQC$2hYoӰz!P~[WzezOQXuJ(<]VڈMF2tNpJQ\)$i`Bvn]@ F?foYԹH_.xcfMغ|Qܒj1#-q{XY_^оK9V;/uP$SzH16..B5xJ޹%f6 Sa._k&)i# nk.؝j*գ/4`eH~2SQY_s|#Gp8 F<(5*qb &.l |þ½gU^?D(dh݅yJ\ʜ_prm9Ze$LUޫ}0tꛝD f}`68('J݈KyQg_% J_vB=\V"}po4E<['/KF.x]L?V;(>yj5P\iJ}-*VV)J-us/i M'?F#}ͯգW~Gد]M a?^CIo 6t$q9Cכ\ tNHn^]@43<Udgtow" ӓHveYm+Ҕt(u zGʰy`әFmݦ]%+=U 鰪]!֥~E-NW9@b.:Y--MPY> &:"\Ǣ* @6pr`I}L9p][thNٶ${㺠/y}kv8h!@+?¶g;C 4Q* f~+/0y?%mW8$oYO3ȷXؤSbA\2&/>CB0{кzm.j. G rݔ~p.@".X wnɢzWvٔFp/q {$"N WoUr]yM]b#>Gӈn-0pQpw/=\G :WFiZp44 B]w)S3QH<:|hڐH$Hç2a]{y=M<% y6&NoMxSԏ߇^iSOc">¬B*6E7hsh\v4bLos -xfS7޻p2(ɸ1|  i\*l9!/BR ^r _}H^ q4ڂeYTwُٹe/8%(݊ N FVukTj';#*d/h%C ->G5J.p^O~n+²oTt|tap,gxj:!q`2!wҧ"|0<^џe fkTP$V?$_ώ"tn+m5͊qh=:Ak 8mq^5VG $ȼ]Fsj/t"w!; 0_3߇_[zCPMoRu]f'7%8f_FG G*"|uEot7#MWUEe`7Ky= j0y) 8Kyr"CS~l*qt)G>| 1̩]g2󻕑'8-)H u<:Xiy 8ZݳMVlfCVX!v0Zľ'TG2.9ЯMoHY,W0Ԡ\[&aI܃%ʀmxw*ԠOɔ*vSyGtaʩO{,mMP"x7mGn0jI|7AJ VqB5AXE,K[?PA3Art )cO'p *X-S7aWp#m9l[? 8>e'[я"S@+q-{ITyskNX0mqrBtS xmCAq|Qv,},gpn f#Tg%]k"z8&4Vˌ]W&q?k'Ŝm!8TC0=*P{܁drMj5-=0R/5ztm1SU{̘Ĵ i4{ },J< |’ |쁿m9ۻѯ,Q5B )]Ayba(Y**b6k<FH-xrck'n;a1mXCUmZJpt/R(> CYU (1-=%|o4L MMSó6Zmw἗VDgm)N@b'-$Ԭ+K1WsR-RH Ɗ"OsAFKTz~[~,JC+^N_ 2q94]Br/i|WR$djǯno8V19lVP26bYj,"Xj{ Sƈtcb@͔mK*/X:{#=b!x}띕wj(Ju^$q+CU oetGh=7,* CjSGb8b6v-Vd%)X[GZ;1ll wq+ ( 4>J)f^;A?6?и;Yd ppVG%BCW/p e}g ftn7y:{\Qe!~Ncnk}$d\qȊ fK:z-rWU}`ˁ:v,ZLfWP?4Ei!O#l@喚8Qpn^ ;ҠNiB첲Mks;/n0?ۥCzs Dj|qh1.H lpN3߄n⹎M@ݡa'HW{!Wa߬)E}8 %#)L68q_z!f"%2AF'M.{%j%4Ow`\Fl{1t𩽮uo9bOjgWICUtn9AІO͸d430x~"֚(X$C t8J!DeR' Av*7uS+1`4 }GaH!~ \ӳxt^lWΰ8X؄y;)|з)st ]!sVl 6Cho"&zC5'2ENa r)_$\:,$zj Fo i5|pD< ǡՈָpj}kW%E a5}seZsO 8#^muzK:/JR+4l)DNtn,GwWEVGg&pOĎ&S;L[S ]\n>|GhNdɝmZU(\tREą"}[˳f"1 zS)?}XG}Lj;eu\WN "tS?AO AZNMFB:Nn;9>?<R=DHc@zfW YyV=֘6zט V ~h,SQ:q؄a<F4@Ɗatpny Gu/t_:+ 錝DU22o3*WKv5 Х;r$ͅL <Ѥ5vklWv=SVVмΈyu^t:NE GXrH%ouFθꈴMi|"?{wZ_/=F6f́ZUJK؛3d%t'p&sd~-#s)AfvFfQzܲkݕZ(nʗ0S]f'YJ|kcfCh.Ui_P=KZ+=v;^ pMAS P<#HKcYj@Ho(N+̓5aTt(6_#,x;8-ٖ%7Rڕӗw9Iǵzׁg~0j!bp|.8hȄ߲y||N 8 tnf\wDsL˺!o{hՇ(~ZJELZ{np!F,)69g\;6&s(.7 JzK@8HrR\ca싄l~fwWA%"bzC\aR*iGinr pr;h`bg 76[K(?.MJ& B5k #o.r 592ܾ>'?!@,m p8 !D``'2: -j=m܉pn,mj:=h uIAd4, J' XH P Y*C!-,(z u I~ Iq4LЍEndsdgãuMG݁ ui [s,BɧHNG7 'jsACQiCXsy3,Vc+ddgF&r6̹CQZRf~Sw\FP,?{3)vjx$$2)\d/w,UUm$x bjI xl"#zeD:w,\"8ub ?{>/3έE/f~&ΑF ~Q;9F Qa&eW}m Njg h?s1X$ݮ~AFG|f[_KI\B>}Vw|e2"$`HQ/S"=R6\)K7!#.j*ЕR]CAX{Thb9jCMR/;%h^J4 72xl|rwz\j'2vjx-  VmXWu)e}OWI;U7vLpJxKJ~v1 9+B;^Ј~>AL 9չœ)PcHj4ӫgb}K=M-n_bOͼUz<jـ&직XYC,%o%:| 5Ɔݯdt,UZ?-'NI] RM#_fH@II@f|Vs^QxZ k[`FE孡.ErB5b}xҩQ}5D7o}X!-jTڈ蹐~{Pݼe:x@n7˨Ϯl^d t\s$) 7$RDEZ< BSAҺ'rIaZf/}m!Ss6}:ztoS\Y{ԥgpj2F(H) "kK4Es$fV5ox(^jԍɀ_:UC1c~%{S9TR^eXNڪY_Ѳr#tJ׸4ڿ1M\ьoM#W3W83*aG%jYDۑ/, o2wth-݌ںHq)^<-pm&GoNor.Dm%UӎR.ڿ,L 961]a69AH/&|ga[^͔p'ޮON~E}*0vL=SS{g-9")A8\YVEjPa[^-awJ`"H-l#borҨBm"K1 JΉԟ+ئm07 Lᯘ%|v'Wt{Tʤ^XނpK\VFb<ђSag`wluST ̦3Ү!\ ҸဆH^0p0<fF.wb.Fe.?*pHL 8^ K*&{Jls0=:Fw D٬HIڠ3fMEM)T+m4։qYaSӣ d$tVԚO؍XƁl7A0vTx^u Q@K=G÷RYZ_tʱ5(q&2< gPmrå4@[YERC#Qu<76oWrܑȻ΄\e*$ m~z̺b |6v<>CҀC\lm( Y D }<替]yVxC[8F߇|$7vrIN; ^}^|Dj&['&B/Wg'U. 4qF iYݛr؟ܓa4:6?݇U#mҫbQ:DٰMs6WeX|Z%{Mqyi+cͱL/$^t)jeA#ࠑq3A¶ *S^m qJbX la˽Lu嵔O BUj"e} ); g<(8)jXdo:;!|lr%'}|6  J X5 -yڼ >bbIyX1ٽWz >7%Zf?50j w/~#ŠL[W7.,AZmy]fM:?6؊7-BÜ&p5h (h AOh;I*A?}o о8x:50Zu[#VDK7V^pBmPn-LP%}a3 l<)8ywV?niEҴa<RT43Z_Ԉ-[Ƙ"%|Sӫ"`/0~%-~7dA6vh0]a0qx zjg qOO?n1Rp8UE`Wùso|T)B>P9Pېʲ|H1r#fhBwa<ݎkϲKcwGq i‰߉th4ƻx}[p[hNMd%iաgC؆9l;.[TEJt&f!Lo sq \0& զ۷3\ &l`W&1\ov __F),\FH`|hISЮ}ǖ%HwR>p}_Sz]^@HEn e mnA`/;U؏sid<hMʼefBSaæyԗq]Jk8 S; aپX55ѳ/U.Fyn]h"``^!F ry7HȐȸ [5ISsD.ٌUP]m=[e|I4PNo):m֊n<9Gɍ2աLjҝ/9~c{Xb$`0Nw2C?C<,iI4p@D .%xoUr$z (4(.Ό m_MS+pѺ"|#U<1+jb0IVB[FSO*| jx&v,bP!€q EED(@#ѨD$u%ew\Fּ4:5]~H6k% Aճ_ĮErK>RD12^f4D LBc22}BEd!ޱD}آ E5r;D:Cfdosg؃0t;祐+)+B%[H0IvMkw}#>^{s|3ƻщsUPHw4~8 CG/Rl(- "64=<fs7F!Զ1])r'U}`hVd_ %M=ݡȾ2\^IskR}X6|VfҷNv[>kWvTAF-"FS0z7h㔖6ݥ\gX}vv3s@k:5qHV9eY^>[G _uAB@ LK(a@}w=UY!YIo 'yB4:Ri1R'2T\+zqV<xG; {ȜL4%O/ ˏwXGSӂss udo7 rSgawM*_6p$hR=>lu1P3$?b-əᇈ(AFA oߘH{?7Dxm]S+Mq:.֐$!=@!v|GFBl;¤1GL1SV҉ BMSëH= Tn+X"wZgʐ%[%j5܄֟͹@A|r!>6S2Fr[r+peU'_BTpӉQY\uh>ߘ).%mDa޲Kc)ݻB s9i΁l̫OLw kpA$Pxl 4Ŀu&e߫ ,?_-6M1}Ek7Q/Lظ/AR 6Zo]RRgl%GEιiB4¹mzm %\A.ɂS30₽҄7>{'c&*ș.r[ zزbQ#j9Q/9 rb۶m۶m[۶mMl۶jN+=QGU"`G!&AGsKDev7pg+p KGAN^qG ɓߌ5ϿetDPNrJ; fDB{O[Q@wѯ(P'ž:MuN7=gΐpcYFYN(tYV*!= .o`'ۮ-M*y+U+D r2E" ?M%]`6SCߘ7atx+RsЊ Vܡb/ D9ԬMnΏro%$>Rнz4X='2@Ua[Gق$ dE1oK#yVJ#Qe_c2X0?~ \Shʄ!or%]&v;V/b.?(eVm,g =]`őW3F_|9!*[ L\ IϾVY6!ER gʺ9?4aLE܁vQ@,/F fLb/Ў` C9}Y{H֒8AƠbC% k:߭483?u* MB2W➚J%,?_K%1?/=GoG_A'XH՘+ݔIa/AmMAM_-;AUuI $xLX`n^U HSʿ(@OuOnl(Vo5~IY;65Yଙ\l@N(}|@XG# \ ݫ43҅>SW#}aù+bA0&Q:ό %HRѳ@ җ߿'o4we)JO45ΘKIͻ;-7R!^>ori!26{AY e90+3!ԝ3fQB/dM UId"8ԚbX>M4 5O\l̰5Uk囻<8<gjDab8]/3a^qB^¤Օo#+`  /ٿKM9kITiP?yps:b 掄kqq[9\[);hf@Oq 3QLr {*|P\89ŗچtbq.gheֳ[0;D+050&ir9z;Vh}<#P}/BQ~eu.9Y홏3ܚ]ʬQK/ ek J rI4ooEy/b.XOQo;@G6 ~Lv} ]2'r"<8˵9ށfK3V݋}@h23s۹ ٣%.wu@6𻙘d_nKWwkns'RoHonEiT7ڄCYћפM|PHzg[P$zasho'X~'*o|;GǵGmܼ}?>L3`bSC^tFvmo,Cfǥ>4Tկ} jsaX߾!۶ q? mi%F{5哆ĿvW9zPW+sbSUPzʖP'fN H@P`'3ZlYة&~/&n 4A~G{'}6-9#{`Vm&"Or~u YбNˆSG7Km+a6riY_y7?qoXPz;^r2{;m6c9}/y\ UɅD>Mdh3uDtk?y9Eb&/YI6H<(y &iHRu=ԕ\wMbL٦0O`(/Vgt1< *G<;BV+ow bKrlU6]0uNƫ<ݙ&6yIePm{[[`UGY(P?vݢi\/jQ秔_e.?hFHqZMyq:B_*Q4R@|[ƿxڏP VGyqj)݂^T*AR bK)6s&X|~@tN*K#c gހ)RCKc86A"Wv/ViØS{wn7a!zU}|$l 3T/Tc?8!));7Ffr| .~? sRlO) e xv I{;wnڀ~W~"mu91|yZPu%w$lJyW)!4%s0jܫٰai׵^嘶zLBbX͟gOD-m! Uߣ k _jjJ*4Ρ98>9 z3Hvo]6_| YVFhـ` V^yd}WvlKH@[qCJs;f8c$B$q\,FXٱJ“#͑I4ign.Q˸kl"⹾pʽj)7*ҴBwf\`_zi3@av6@m`!xzV˻\+f  Xv׷Dr_ԇRE~%kV:)yGawI!ӍB/ȻQ.! P+ NPቶꏞ!X4`TS+"g\ԒPra^-KAYb!BϏy,dE&Q }c;G i1Čh>7TqIp#cYj3)Voȫ(Ҫ=n$܎u9`\(56gmvޚb8; ؃)b1|5G 8xJv›ZI3h5UXǣd 킪|2y 8!/;6?_4GBI+Y_@;Xmf3(3nvKh'Yd2!=d [(%Gh 5Q M9WZ ^simB6(w7?׭sBXb;Ɓ dZs)#ikb29 DOM0ꉉFkMg)NT O8iM@+%U`3g}嶼)D*@4dYݩ̢vT5 zfc\=3f=k-4W"&KtW`ݚ`C23C(muf烴mvL%DT0l.ygs4/hWGk+Yލی0W= xDȡ! L$%0҆:YME'}HT":`@Gֱ~̄dq)SVSG\'xB|Cp~ma?@|# *ʲ[HKp+JR`fH amfoLaL4 ! sȝo\`2#{L[:O*8烚VCyH0TF(:}s ۻ⇕ O(Qaf_ *VdQ{@jHR)׫ TX$?yLu6tx(=mt|IRv:Sέy^[\ڿf;PKɰjQÂ=~x 2`}g}.$ަ\$|:QHԧGjXh20I?ٮ$JAKZyK\axoc ^r4/f}Xs+'=ר/y;$Rǭ( $5د4gyW2v(4R0]5յW{Ai5" 1.;8}C&+S_]n[ ’-ɗW:쮺&X'[i˽x2_'TxwǰJ&Q*Sc?l Hƍ|xuٳHh=}BR*Z?irjaL@ّw#RN^g=[ď'` &I{5wmWUw*"64K8-W1qd$MA 4 6:-cPs;9(+',XM""OEdkўcpUv&ӭ?bc3ϊ)yPeJ#S-"!>Ap R~z"~FT M(D}ȀT4_'M:VrśkZgS qjo1ccO+,?ajxOzZߘ%5PMf 9ǁ\m4arBkIQO86֧F`6VI6}qZ 5A+ǺMҩD|(+͈o7of>.Q%&gAOv4VXf18j/4?BS$=H"gYy|Rވ9ŪXnLH5lƶu,KG!~:Uu8") ŰX6-e*Mq>p\|ҠdRt\boeF8E]S}qU;tUޝC>8 _ߜ$OZ -1ѮwנnD |0'AGh5#ivZyokMCFi1/5Gv&k I` бvˁ"[~lS%Tn\yBcaB%KXNJNp'qASĎWz'tN;xLn:㬬M<5%:"D.߾hN!G*M-%uyd2 wW=zAc~(z\(^u3W~"\))Mtmpۑ.Nqp GUα;᩸UwtO=,6"쵱k\H+/z: 籊ya.մ߻{+r\E,M^T($+=%!DZ-f݊ljJ^Q&d3Ce(nVay̽[MPuBktywI]ek|+Jƃp`KғIt2` ^Za8. ҄IuM eU~IM XtaxFtT["TUt_0[:"~ĬFu3`%Y<:@>(tp~qx|&XJNfz /l+1 z4?h#uh7 TSF?Mx .ƒ _$U)D,m3 026Y}R ۀH,hȀ!f YbWtDc1XaZW~/}MQQؑlEUx?lz>Ds#BP=)9-&GB1t380bnR7ʓA}vlJܜ/#SF HyIْ*K\jjV%J2ޤfifs'<3b+ \cѬ/4Vf.rsrO/mFdLȵwdqޙPdn=163)}$ ށaѽss[2g)@85ƺLrk$)NEO;+2Գ"O5A^Cal[Sӝ"NhEԺ,'CzCc7v5[f"~x;g\(%,4{I93N PhS1Le3!j̸L-/jCh~LpXm,qT ^3^op*6QY>lL+Y6*Z p] cCSΏaң=MZɚ?xbzaJHZ9+|ɚwb-TlL-6NGY̧VEI3 ˶Hs.6Zf8غnA?{dQ[n)^ +BU͌(G6ėOGo0!W(40(5ޕH*~JvG31gLdڜ.8Q;?@{_,gå, 5s7kb+(OGV!M4TP~Vp9Ztn*BBZp&c4#[2ĤfN{ju;Nڔ ⍊t3'Y&[X]zԩkH#CǷD^d-@%b jApjfiEY2T>ZkxF(8_4WSœ孌DHe+pMoAEC;b߾X篻K$xH1VFXN{q4l<Dz?{K_*^SǍUBIf?3  re0bb.re'Ts]k[|U㏫g-&kƽTJHxi_eї۔!:qfPt|'F=Iξ WBJRI?3./V2 B+xDvZh-,)sܕMG;W.Q$d GJY,Zi̢j!8E ƌ>K9.=ꑜ:GR.1Qcc84:kacxF3VF`v>s1MxU&XMb\=L2(}Cc_ظ[MpE.$#|DڈRM$K?yVChe Dű.<\DS 1Rι7v/&5RnN9h[aNitVaĨE&&*Bb T!Mm=.|[G.NDYơ͹oK]H7:Mo'k^F#$հ}]MGT!D;S {OʉEQ͌/y\5`kPƎfZ0;yA \ n|Q]lJ/uAcUq -y,dj?M-оWƖ<41RhxcuxPXSs!Cf"U=*NsS"rGqfFo\L3usݠȌR0cDS_.n\7Gt+ls@=Ђ`[m}O\ Ѓ- ?.TU>Y3V7I4k˓[Mo=>&@~6tVr4dX:-X''%f)sčʄuRNLS!k ,eEYUxǸ;uFzލt GgɠMzaٯ8i.ٰWkg}"'\/kfy, BFC%I^A ^2/S Gg2O@@w<~&'0 {%-]#pSE -)$ TG塱'nkݚm<^ {' %,QpXĻl7>Kк{DR`='R{I1aaMr0nrq"0SbEcHiLqyc Wj$p!eBc%IݼpS8ǚ-Ȟ,wsd=W|ndgZ)_~ :3,xǎoCepe'B aÚǴWshNuEhktp}Iw)*"iRۘ쵰Aec=Z4Axѯ^_XK%r% m Jxz|x./+AtȽ H+Fgֿ6WȠ"j/ZZP3V:ZG=$!9UoYzPiM Ydz " ﶋ)Dr3wc?+Ռ W H.6%RlZ9Y)Ֆ^ fcd*Vb~KP4Ym "\"hIrM EI`pXd`~?;?xw[Ǯ7ɸ9G.00S~UnI&%7SǛ'rͅ3%d|H9޹($A4z%݈9VM3CLFw(eZ;2zfB]VRuI $X+_ *T2`vZxB1Wc@iȨ#0=)e?AR˧sH榉i|0.I_yXq{Hs>qZzVPRڑ.WƢ9"t^Њ{|W*,7s*n |×Wܟ="R2(P^Ҹʋ"r2`Ikw]2gd3a0f7^h;M(.~œJo@;x"zq,eG43`ق#@]7ɘ΍ <qUOl<8F1TϬdI;4skGrҪ}젩nyj5:eDް2{Z~蝣!\= 9ĩQVie+Z(l<Dž^8Z s>-$S5eϢt,\bSpDc|$E"E5=YHp^U݇-L8JüsSN: Z&Ϭbc9ߴ]T:hkĩX,TsuU='BGV%{W}$zֵ?zlV3>.uM'T^;Ⅶ@74ʹ?]N*vg]}F.:>LKc5+J׳rԢ tu¥g:`p-;ۧ"`~\},cź{%= >f$gشWWf_gz_K/o$iAK#]gA{y#bLZYIN+׻;% TXVzLqDL Bn2D&,F1Aek5@lPJ/2iFsW}!J dȁT|Hr6n@VԨn:o ;qR=wHS e6LK IzѭOߴq;SLuQm{hVqIaiYzQwS`搗3AI/(L!1d'm}L͠ J2 |{'fI򾮧6f/懤t#C gzP;K 5cą) nh#F}*tJBmof=ʡB?m`U$v>Jwj)᾵| o>M9^ٮUќvVvY]aj%JdWBO7^VlTj;uRf)v$OѴ3`Q4$ufxio8Rz"c0g_+SuƨYWU L4%Ѵ% 1SZ*`ryM)3#w n)R75r/zKT%8VbsC Xv?EItM'|y$p%"T!E O8YaO:vyHE{P,NrP/+z]%SV"p~_TZKeSߌ66[!uYβ:b‘yD/xjKj p?Z"f~=~R:B> T`F:m=GܤnCjX P8:X饧9ON9Am++X` Nx٢D/2sMϼ2(jJ0O=g=ڽ4@٫-:7oFaĽ$fz#ZF;y+/N!R&Y9ʱQW:a+o*6KfqwNRZ*| C _ɟ'EڸB'YU=yi3yQ蘜Y).mx#5=Oc)* WC 2>-ՋQ*Cz r\@8G(HPb/"yX?[&=r ϰWP rmbDjZIgXG#wHY+D?g!sle~@Шs.@Ng`w&#ov^ F?9&" VάW (>1"_\^"R֦a,ץ<>E JvdVP=9fMOBJa,<(J]-^ݔI>Cϱ\Z̜ 5Nzgbw(#E, gZ%8hGN:|aT WZchmi&SVPX]G/5\Cפ'_m 7Vh(bnp++ P5nߝ0qwa&{zyZɚ_Ÿ10Yi{ gF<6qEsBQLc~`]7Gj3hc2MLo';X[z:i껶,5 L+)+5 be=Lb D͎Ⱦ1B;ΏKmSA^ȌcOF \ZxRe7Jpm*TC5ۇ@A4ZԽ/Cv#cy *IlU!,݈oRWbTfL[%ո(R aH٨/Gf57]kϡzt1n:[YV3%6k]#~9e df=rQb'.Gfut?)3#RmD&w`Cn+=^)Mmڼΐ%8i 0J8?T9S}OEƬZM*QRffCLU=b4د{#1:Zwo MTkmm1;?8hjS-@+jd %y8LM1YjShM]ϻ$l%)#h9[}| y) o{=A(j2FSUȰRbԱvW"ƠFUeD((ev ~|#@ pd6]J YHFڲ˞"D+HY=<K^W?u-9@Dl!M4 ?`bU#ET{WGoX7xeukuSZ!>fP+VMZIJ%!?\0Jnٶm۶m۶m۶m۶ms"fb!*+*1$D]2a.V;E\ y hxRô5:1 XUw5^"qnOD@$";ooI ۫ P}ڞSPE 1-htYNd ?1* 02&?8T62/_b], |=E஑r>q<[b:$̥bq:!P6$y,X_TmV3|]PD8^FxTp6(Bb0n=G3>nWq7C4T] ɆHgj@oJVRhL=f0>|2fT1b*M9Z1m<Ѓ>09W3EhpJRT'gA$T~tO>I꺱'/ xE32N<*pH:Kx>(b}RޓYƜ$I{cd=y.~9ss8Nl l5/iN΋~3y3[iu3M:m`ⲊBO x6Uge:{0Y&vݒ`4 5SVE6Gopy#D Q=D|[4Pr⧍RxiN-8ϸri*zۮZAP^UvwDj E'30e|4LfXh}1Z o"`|xvcmĨ #<8C|&%W8a<nROKdF$W) WaͿ`%26bԐ1";^!xh6VjʈgNUx}7X y1P`&"Ƃۮ#1:D{QvKHN6J񂙐Wy::@bqV7Yo5f:g8eѽFORq*kxΨTV0 *f5^IqQ&IY(䂰=b<]WIJ_QϮ;A oJ#a]4h$`+}1 mnMF U}ǿGyZT7}j4/C9Nzבm''1Qq=f9r>ʬxԐi+=&NM5 N,FE5<4quR 28Z :āgO܁/6_,@r.46{$}*sYìd Nt[lwS6vGa-xT S-kM ^ՅAT}(Ϩ;zH;5.MLȦ'f~vo458a Yf|u;j`p[Í.TzG&-x<`w35*5=s!O'MCj> u%G$"@-CƙXN"&'^Z$ [q#>s;Y]%+Rk5Tszn LD%BEl*A}jLSIT*C֒5R<7&\ &Ӣ\])O8NkV0bQR15XrM M&p5Bc:&ŋG!LD0k(V"8|4w5l@o `+={p-|*G!y5a.jQeL޽Uy@yG;b1fve"ФOn+uʫ9ACX\5^q""5"J"wEo5Æ7Wem I#9"K!sxozV@F )} ޳[Kk2ua>*XLZm#$:{BVtS^26dS0*,8(|HpjR܎b*UI9k Pƅ@m )bv5K5 ę6.mUWu);[a܎DqvAA=) {h Z줻΃0 اi'_IbOl;sl-íKe)uuG|akz7eԌ=P>Qkz+l÷Ggkå`#&F[v":hBa_Pຍ}u4*&R-tVFW(nۋt- Y:@Q_Ce*D|/olTzgV;`&p~bS:xE2BPJYfo(=(u/NYJ,՘}+,Rγ_B@R#bT y-Qo,H .sjoLUqY쟿Cwq͎r Y8-*z|qVw2ҶDx2LJv+x.ʀWI?)y{s2F^ZS=|DzTNa"xF%"sT2ɐ]U>J1 }4zqtR:3DBr=Ml&ZJtim! .s@;?w (qʏl3U8 'cg:fV阈*[ l#lqz;,>B㠟zvHO**鴱a:?藜;ː6lTq7IZueQF6<,p=:][J._۰wmgܱpag5嶖s__ fA~jgLfYIL+_~\% mGyGì,عrQa1w*sڜLM (gluGFTRaiu'צ8 xaK#u؏&FAdFaw>e >Uv]{Z` yM,vg1}ȼԡ$O2 J }yQnhGkjKH_ >>Dvz?UΞqOUqYVedq`QdLj!؟8bĴAcf`yr4V3&JKBٵh>&ҋ9hTQՈE#̴P"pBRj/K1ғ2 nvH›6jXm˿V^[-saݿ*ASM?Dp垏a] NȦ4:(]d|ӭcl%?)c\::}ZLCO@IL} wQ s&:~ϠZi\Ad#[o]'_ZFс6B@i-ٍ׀tUS{q@uX| Nʀ"Oʇ1PcG(JƢWqJR$UC4FNAF+= 10ݗoX8=6_5 A MA9\^ώ7wzUx^ZQr0bt6N\Ɂ3l._Ex 1ATz^dRקӑ5ˆ.A|wY8&]஬>̉fR+L2Pkt5\ c$/Mi!}ܡ4ӺId~=o4b%^aPY- p=]Jw|AFRYaW־!iհ?(z7hVAWH8/U%VO׀x L\s8N<3lajFilg饭ěEPo|_+w9}" %MiYs&ˌRRz=y 1" jZJ[o50LVٶy9q.&վ9[ixBL!ʞlb ;jY. @[dV,''i 6ީ0ѐJ |NnJP6!zo(fg8b11|Uı2<2ɱQns+ eqHvĦ^9l|英mDdqZ LD:ҚO>>y¼e1gB/.c.Ά6?Oc@Ayv>M6fdl{G}PNHڟ6Dă84l*_ӔNs7MGcțr0rk})3ok]cwWnW  >'Y8ՠ/Yۜ60>p t44P@3I[u`+bЇ0ކŁr n o.Ru ὧֈ63R9JZ4<6~&ٷ |W1V[p{ZjMwdsfC5A߆CSmMvڂ{aV8p\/WA ?jV BJa %A^|:I-,*)ʮDp:[ .hGH\g"yE`5IN~& WV' 'ybB*T>Ɛt'ُ -lԌAthrQi_Ǧ>Y1MhFn*a&Lgtt#$; >01ljI@`oҹa{"@(;G4*^ u^PHN,+]/R4 _0a"e]g,ZK-ZE?'`w|H X \_vgxљl r(O4! {^YZ(6u_H ?yПR R{PȅZ=[C1Ì4tog|V!]7C :LI-l\[=l/_HHU{9{}vGk#zN(,WTP9Mb3\7L3H}&J D(i/Y^8& fJ^wq^œN0WddN}C]ޝQGEUU5*übUΟi4gS%+RT64! 6_+ƃ<Fθ}G YeG mw;w.c|1EZv z_s"-r+Lr1Eʁr,ݡݤ=p!oƓKa+r>GCdY^cǾquk0qv.&a:u >1W|vnly<]2`QI{K]u DQ>1,\Ϣ3 M+rb|oMwo7% Q&kPW }O lz=%~ݡcƈ ghUki<\֒tRa A*#orءv!{+l,7(QؼFK'W/a?-F~fwbKUkoL>꺥bO]z9`$-)6;zcZ&NvK6.1տ8>*= VYj̄=PлA6 [{g6֕7_Ḅf]z0wއejZ'ġXw9&\֞$JnS0?tTnp)c۝OsiG n溔Sf9H[L鼺yZ"?*A|٬?chfYhUg\6yyt+ېjI%jTs3%NS Z<):MJt8r2v9FviCzKcZ/,{s koa5iV]!g vJ.N qyp_O$b n쓠%qOho;KL<2SlRs U+b6Pw9kQVi֬qe^11Y:K^8ۺ] =/`I㨝\w7/َu1`\ _1np%SSfQɉW"JૉtsKpFM$)mtj ZlJRNӧmT+[."N{R}܌ m2R&L1Hk[O掶X* ƛĄ%tU~CkNO wΐ8ıc]4DKKE䀘4`ݬ¹CXg@^pJ¿HCk8<pߧAvK Vzr!0?!`>m 8>qYm/R(q'"O_0Y3ELKCEGuep*3\d&+ap.y)fC'%=:j>CC eQ=7*U2qOz r,g(F ;`, zQưCUH5#x٦^>>?x 6fBO f5V#ir>1O3"$99&0^:P/%_P;tq5OA'>}%H Q:Y g Wiru!qT9'GbXiu'Cpe7SM,E~VbK'x#l\N k/LՋ^KWvvIJ1Zg{ ^ y?OѼߐ%M9+\LN(e6[e}u wæRL'&`͐y^$ڑjM״@q\qb.z _hkL|Hҷ$Bf~读FߏOmOUIbD5ݤ[J̤VEA 9&#oPS*ku,6karjc- @Ð)jbHPt_Wp-sغle$)3J^O0xcMPZuoA~fZlu}᝾75^$߼4+aG>63RT;෢ZwǘY_H}hKYS!mvҭ~ݛAam|3n8B]+/. a}H f䶇bWrZv:5S;*M i:ZYGb{y,-iA]v%GFjk( 6gSgmOG9nHA8%PL^=褬co %m#,cJ> Rq 9MU芫h_dk,ipx=J_5t~]RX| {􊁬mR0G| #'E wGufGH>+z3gQ#%[?h;g{q׹@6wB]2%+.6>iT~g (bgvRiD|\~\7q"3 1VoI*5̺|Wrr)nc4Tj&<5.2sAzKػl|yڼAP>2{Ŗȡ2D I^B4YdR>oXJ#NZg8m}UAhDKc#3"rř= or )zGW`,#OD+oDZ8/q #X䁀5= G/Tx Me[]EW$i&rz6'uf%Zn*f}/4A;i1k:H.jC?=C_=?oI7dσ;E%U0H25K˻fs0L' @>Kn%܏GK퓮?LLMy2Hiڡr"&C&P&f7b;͏XV0 5a'OoE ]e+ݎ/e=/&g/H kߐqzousHXHUbYߑSd() gռ3Ѝ;tV nd/$u6z<*Kⱑi}wlGd"az=fG3\|g@`X;YMs'SnGgp -~U"K3$ƈ%\ hqҧ\V痗+Xnq6Rx'nm{NLHMV奘C"^+t|Nk*w};rFNl\x_홛]F\[ב12eh+_{V',bR 4<^|. "My žA1;҂wLHTxa;v [bw'1(XX_J\CCc BB\iJ~Vx w>sxhoF܂ԕ64vO6l_/dA]ҤmtgC+Y@ Ⱦe JOI_NT堕푽?5LgHISyc?ORpo衧T`A1./=,åQR CO '2C\+gͪ_M[3X 69n {HM{]9* BҴT؏I를bAVeM~l!Wf^t?Oʠ~;P{= Jxe,OҒ=) $@*;sxTn}!zkLjs b?o@״ȴk RUy oJ7px c8ꈳ C .7UN XIB|peR7 ܨᣣAz?mCkdr>侬6}0A5@ DQg%?\0 4w]x3Oc]ggd^}oVT"e \ŲC_&ڤd/}xa] Qhr>2>c\'ӂ7{T_4?FYN7j3L~.'YqOVxt4r LLa T~P;YTҭպ@m'Ϗ[-E,!a~V͋qUqo|x+C6e0ͤ-޿k0Y-s@D`?9t]ayp<FSP?ٮ>*l\^j Kd:r%ʸψ1s݉wo}pd$ln2߾YIY@hw^MpmK<ŗ]\Fi iShϟYE{YcOLC R!(Өt^s;F^#h,,nJ˴׊}Ԏ*}5c4H,ϧtpQhggiu\2IBOƩg[p3 G&}>6]5!>Lߋٿu*+[FN=߯ -~/'T*qd=f4qѷ6Ә"fY>R65r~csƖ9^7+YO3D5C uDaaS Vt)A5hS.ӵax: v&SenU)O4ahY4Tm ϶sȔBhE]pLժA#uEOBwBLWtгR?fi> =U=>qD!'+"wrr$t;R6{G RW M1XeJZqvtcJ65gy׮YF xB$ 1%Cj9nԻ `9wSje~=%"-0"j$xpF9|+l=,:/Ie֯&~rS-%M{0hQ&FA=nycR]Q44%[5ʈk |O6 TotSXDcY1X ta9,-6uEX; y=ⷢ`MM7 y!nYh+1lƁ&6ˀn0F6D7`yw~P:4R}EwD*2M96hh|Gmʃm;Mҕ:!g`Pl'Kŷ>q:_a63;HSzޘ$a۬$i SUI={t `*?\4M:۠B^Ùޟ,H|!~NEd 5wnM@l1{ł^}$V>\ii7.ͪRyȦm,{Z΃RRedDB=ga"spzYv+TDړ9P_~91=rGmpBR)诖{8`'U۞èqT@"v(̐R(J ਉbȏyɎR&CMu> ^0!5s,b؈XJX6 N77NNTW*)k5H}sb#g)g'_(ԓwWMJOp~ΉpQl?Ŀ)BLpXLX}EnF)!R 6x;meJ7D̦XsG4uk@< oB`Aٯ 2сR\s#6fX: Wh-TF8d OIK5T "e?9:3DG.,C E H9YO!b@\osta:6% 3O@KKn6C#찒)a?Z` cpd]F'mQه "s`:s[5h]xl܆  wE*.f( zEXpK07 os\*M.NK]%]L32̋\rC7l #SJxY%:n GAy>5.*~ d_G}S q 7>z2ܪNWj,U!}[ sW*E%-~ G싣>~jCخ3E"]ŝQhK\: A@_5Z֒o4"MgN6`GW iFXä3;؊hrcX@yHAd s_bJ>GyVbELZoS9=;? H%-*o o˨|~` |4 2Fe"lQcs*Xhp5*Ǐ#!AcS`].:ڱqG~6 (% owp *){"sCyܕt#?m,.S/NpXUxAУHkӸR: .|G3YY6:_W%-a1@N@vpI@x|jkr \4ԐO:8^eHSꚛ@JF+XҒ_YrF2@9y8qa9.|(Cpqv(;o[,?-43S[Tv=Im^dV٩Mۧ=J~ƥ$ \[~CTW>mMƠD qO:uwP%+x ֗}x|S7k Ά[SG`w h'怂Bm{K-ǭ7g>ҵ]KQ9ړ ZBK:Me'|Y&Ѷ[XhZÊy &mKK/OAz(# 9IqfGmoQ P[xR_;Ir;jWHiۺr8à4NTi-uߘ!#^`@0_$o3r5m[KX(9%"t *0nd[c-XIPĭͧ6$d(O\7R#x'U{%9>¾dXr#ˠ.rOUy1NEŃaȬ};b /aAȖ8kIZe:F8e뱯yJ&}vڜ#p4ѿzD &%R/ګt|p+fmpkTJ~#U%%WwV44Qַo4 (r}30;<12mH)x ![8ܽf7;.O3Zu&Hhi3Ց310Nw:KL;2"|Ϯ[DU 9#T3ސG++TSh)o己pp) $a8%BR$giRNA'¶`7ubWmUVP3I[ktY7jx])/=%7k;#*bsYiӵM?->&*`3ZA ?.v!$bofӚ^<׵)++)ޖĺ}ﭢqfO(qʣJ0ѿAFZZ 8{[yz3nG`'S ռVX,BgJv9pk VH ’C;L7D>Gi;:sh߲`߼VX,BgJv8ib)Z41I9v<*kA/-r&+7 K\{ n'% YG^{*c5ӽJJ." t*ۖFg1^|nu'&RVdp5$ 'βkw|CXt>ubyʹ|zCl&OJ 4q .Z9} 0og,YXswr)\$z*V{!eZ?/w0g(we?&qJQ[ v}1W| f:1p^"[!9ub(Iw?=)w9eϘcO6qIuKxPIox AhUx)c,=k?](ҟi K=,(V4buby4ϵ~56y!_쵰xt) U:?GiZ>[y~y|R#&av pC#)pL|C.: }gظ}xޭԞzEjtViw_Afr7C*FGO~RCLHE`UhXv8L췘oL\aMk}X#1\ee`CfRah /Ų]I7ʿ,yjЙ4J1{钑K):f;NNEos5{k[=rӉ4.[_mG훣Sa`Pp($e#pGpեK%k:9tZ|S؁|鍰\ Lcy"W&eoWEhp~dyHº38m)JT4R6_ XD&[[E6U0v2xlӄ궺f%OH6Le 0lN\S(ȫąxM^"87,䙙?%0=JE1U'| P y7Hſ!;`[ͽoQl@e,3kDdB \W=kнWOQQīM9ȧ#;\R4s8ɥPr% tX \cGeqmjܳl9ͿP2|Ax+%dfZ)]@sW~r>qeF{]9>Džu4@!)P%tT)[š0wI=cdž[ EihB?"𹕆=府g\wAGˆ}&VFwc *Rf{w00fR}; V0ST0Y.v&Hf>F,ؓ|ɝRW8 dh^_nd/^ZOlI)vsFGkI4|볠0'[P֑"'+KCoœ:J<_jvo j )he'4ߕW[y52!1{@ʳi$D"KmZAۄh-GZ 1M*io&%rrrн}(5u/P4]>vJ˝'o;b.}KoܧNdum;et1f|CM%Z"қ3i8T-gi o֪n?)<ܴNY{g;W ފ a&A,S-upQ?I=bsbU~Nn>!\:}NPy*{$Z)G2Cr$̟n֘c\.6ۓl%fJr#{ݥ~< l(ID%uM8UTa/SwjITT^_pm琄& @?(0@.q$诌k:QO2Qc4yǞ[__˿}$#q"o. U(t8DunZ=V^kRUjTc7a|:rhPfI.}xC++~- fCi&Fx+#U̙5(L1{?:VڳQU1MGrʝ'0A] 3IИ' yVt&LFgW̹Pjf>(%MT=9 HBpBW"Nha4qA ()ڦTfPLqJI%S8IGR얹[>T2oL_ RU*И%FR!2P-!.Vzw 7/ȺjQ[Lhw:U4J<(ɻȁ7 fam߶UQř;{. ņ 4^Z0`,^X1@<.fQ>MQl.| 91_ 7(s) s}Q2'Y! qy+,;^{ڬB{6F@qxch{NC]d{XP3J<'>FTV%8#P :[k|;=Tz u'$Z(+80WoE 13~uc>였s[}]TqMrJI(' J,SAJ93$E )ҽM[y U!m~l-֖N)ɏU]+.:UX5-h^I@-䲚sr.0A<ɪcq~;'byqܬ=!_Nbqe7kr5LyI7\2~&ٔ+;^J>^4q?z%gqZ笫3ɬoo#\zʦdj|NsmOG:+bk?d-_@,PuEvPBG cyXrTCe)0kW`*t>7*C !+KbF;@_ͣ_|&/HkLn$*n 8d< ~gT FE c >F%}4}mL[-?raD}?!DzgČBUқØ8qF |Xd%0T8ÊwAh&2.a1x^ endstream endobj 157 0 obj << /Length1 1405 /Length2 5969 /Length3 0 /Length 6933 /Filter /FlateDecode >> stream xڍx4}UgB!ԞVjӢFmYԮRGQT_ڧs^ڟNo$EPHH(P1@ (La `ˉ9Lanh8 )e71X D 1)%ܤ*`8#B(Wo7#[7mPt!`$@q`#B a[qQnr<O8`C<`P`؟9Ǝp_ #=`8DcMܑP` s!ki$?ֿ#p -H/ Fa`8lN PS4 qbЂh8WB`۬*\\`H W~*p7wo?uF<H2B&H#w VDo JGQWcoWoo1_W+[n~0A `p$c0@,@Ͽ XHI\XOR*)’I1 E?l5(_bw8gAx27Eoy_#=2v 5:0(念0vXF %^0>q5M~_O_:AO4U03*Z6aQ1 M5$ a Mf 5QnĿ _@;^`{Kw7779 }0 B<7H: m;QdHPlyو79AiUGo`Pèxz*~%%WEx/E@ k&V1c+2f!E-7 wcMK'Sޫq?M9,{NEw>kPR?ubWhN%6bpE<[}" A7ktCNu-2ڬe"Ӕ_ήQ 9دAw0:t&Qeڸ1CD9J ["$?=SG0ٛ_Ȥ95tY݇ ~{ 4gIFo4m8@1QO%gml%=yOiʫ*՚AuOvK?Pc7!/01^нa!8IAFWbJS&T@ㅱk*;7 _Ns;&xh[us/޸pd1t >$|q֏" R:'Vmcjǘzl+uKnP]-Q/ڢ:IJvDξ; TZF돊GJ*[\a"u9%}I"MTHI"5 O#qZ4ۖO)]N?R2 II\O쭷"v#`fH{KGMS U/Wmo>o.Gih&nlquHNtY TSKv3'qpf XԮhhS:^0}-y> U>/tq2zOBGl"!l{ϭ XUQjU Lıjތy9o?m0fҙf+p߭+i4o7g2^jǑ~a&ILtOR!$锜Z١r}*7fю^0TN-1Pq$#T!owEkgXW<ӷƭˢ*jh8sCNEB~$0G kjH- vv<ďZ4en+P O%wBG{=Iqݶr+t >NP$j} eN¾%@mPҬZOZ2Av#T<ёoĘ5cBԡi}syha{.#ŕ/ ɵHzG\!t_iE^s֓Y0mH*R}H}&/8#ZQJ R^9CC%Xd .W͍-Ho^[E+f)R.z 2Wg׎SY  F (O=â],G\ |+Hu˃q9+683˺E4kLFC:YVS$vDr#X^GM):#5.7 nbmomty2K:xS4h$yJӫ ̬vA9cRVU|ԕ8`=Y7jӆb 7ؿjj3zwvUA0C/k\'CZ=db!CG sU[>5d|#|7Nn-ϾFLe.V@x˔8gvcT6PDXkNObsk`zWmVfCɿ.mӹnpp{~jZxxqڊ3Bs AM)Ggyhw._r˭} E=) /tg0lcf.4`Ys{lb$%;{yV Eڄ1)|~5kͦS_˪Z f}mverU 7pwN ߧP83 ԯ9Js=@]hFMGUDuofRߴc#8A^B0(0Xy|}I|3 8QY<-v{?43TyVѺD__g8+6ԥK2ݣwʃtG[I\ol ԃyܒ؛~Ļ.6:TmuBT"x`T3+If6BE{Hiuv%3fѾD MF|-BťZ)WȱS: Ղ3y)C^-O9 k*`ܑZmӨo.\Ho##VwJlZm^3Я>c$$D'z 1N и` /t|1 P(U6`'tͳE]"Mo`O3AɻQwBTNruV,V7 A#oQNKV3K}Xo˒^fL'9I1R#n+&9thnku.6r=8z4sw2znʸ-;3aA{dᦵ Ҳ@ -IEslEMCM^L^datwl$2}N%fspU #T),J<,m$i?!}"@pSHayX1Yndc8u>Mݏ@+Ԯ]l)NPov*1?f?T-lIp%~b%^mC.*rZ0Yw/W)sc-{GrC,K5F3G%^yڨ7_x:SRďocx10\-rZ>* $igHކ>UM,~4GI)NmH9qfwvf]Mo*8hU7KS8:!{ʂ0~l%3 nZZSFGj6?):T ]|9}e:\W-Fis}⥹|dh| yaq>"/v?nM]^)<˽Zm1[@Qf#D/y5ۯ+UUM=W]ڷČ;i1S"8u4趖њ3'| Lת}39gk l耉S+SOP,zigB7-4b/INQQ#ҐtBܰҪohm1EGO]}ܡ)!xzmE5>uY}ǺǺM6[D4>7oL\]UVMj\nox

aY 劮}!~_\$7bcy8@k7GgrQD~&>\껔mJ0+CXnDS /kJEoBXI7y2Iut[F[:w5N5i;Aѭx[zqtlvvH~28C{#pohhL'+e7wCPg*Xy^} <(4]*㫢,~Uxt :8tydBe u7 w1~5 DS?WJoTx|^vŔ.H"f[X37HB&, Cڱ /D?{@ϩf^K= ȅLsKkOWdc֊t/(*_'R1JLqO8l ?]=tW I"? 쑐.m]Pvɓ Dr"$͛Ɛ74֥0) F&z#>2'aeOzrAfŅC\!/JhrȩdLJ_.HhN 29̻ _1\PjI>U5LhoaKsP;y3qPz'i'?YXz؝0qG#FzUs:([SsnQ!~'m3 fZbAʾ4sr+v H&5GՍljFk$>矍χso=ɾf=4ŎU-8OC wl{|d{uDɝ'V{+|j6Y>'J[Cv d'^`o9P~?HZk [F)ޠ5܂`},mw3)wx2|_7M SuՓ{&b4%:TrK\i4lf@dH7fhzO=Tfh+խYBS D`KS.φa ; $3tC.z۵ͥ2>E?>1Bw=$zP,<y{ :tbFuf*?Z!MGNJ ΋T;[R,d֟=WEj.E _\&uy_%e'?m,=u8~q;,f) Ha˷9g&‡JV`ly[mLY{vʦ m-x?«qNrft:݃I wXٖd7e-AF)\L::~{|ᒏ& <ѐܞZǏj2TJoqo۫ R~z*"!Xz% <[/𶋫{i S0K7Y ќ^ lO_fF ̬ =|OAZ.Nf1>%+>r_CɎj6JeŁ/J r.b[)o> stream xڭTeXDQСnFCniIER~g}_uϊ{kyؘt x`8 +' 07B@;\W`"Dll0Ô(lPBBAqqq"6" qpD81ye800D0r!P0@QGL][mP n ?_n ɇG+=A`.+A"1P uMc!䊀c"\0> .BWSUWI/(G wm$1vp00/ !('w-[0t01`H_ xa# O O #0 0"߫۹Fy!à^;=6) ? D?qwhw(T~dW0w4ߡA0b e U `;] B1Sc7فP Q :B@ΰc:F? 5 Ԕ'N<C;тEA  b.߿H_g- cdn, ~ ì? wi?{ADs3pdSZf:.oDɼS/Ե5:jY*~Lkzj)@'-# M"ʽoULks0쉀ʈU%>X0Q:ĕ(6N+E=Uu.{ G`_o[]sb $t/wP^6u[" b#_uB6"4qsq9&ҲL2[X&6W:72/N5ݾ]c uT,I$RV\J8dҐ@o$btŒ@j$0c򪦪Ot%ȗ7A"<KG׭17|ȰQ.#Qh54I=n5L ,Y]~l6O57ߞ|Gny6+M$ٽhҼ_zGҲx!w -%}ttf~3P䓃 xɽTo!U@[_7s?4ȸj' :c]UQQ<<ޓT5Y"Eބ;!D'%ܺ:;4NH󠳄 +BJyf&!yafr˾إ L*5:`ѣ_ .g^؜.E+n=(VP%y)+7 ,ej?%9OҶ< Td}!/[OڲYъX 4viVa pjwY$C{'P}p,[feHQ9D<|P|XMDc W"xbN3MmcWc( VEZ>?]72<: {2w#ɣw/!So[WETu¥%k5Iܙ gG9ĝK;RkzIIrg0ľJo坎MOUOk -_Lv fmne}wo^ozzXLy2 >L0Ol/8M-ss=rS<ܖ#}=Fpj2ƭ%h:a̴]\kCS`3bZqQEgn[ k E5tk[=`Q>X;M6r*?W!9qzv8FeZY?ZZغBc:튴3 ݇BCKVy}@}SMNvO25,37Ҧ^xr"A-BY82^A`x.rE,*P1 "2V ԙy!9SdoMPǫi 8~cJʷQpD+UEs1ֽ,5Gc"=&wSCaF$Jc#2dJ#  FN J $^z[iRhk|Z츃wwHB̩k EE oĻ/[BQL;n[F9)xl౪"fYGg2kdi/3 Mqgd26\+ٸ ruq;p 6:s!R㌐cKv1H/MO7""B^߽kC(I}o[uy-)lMOZC-@}.ʜ.SO4[,{utu-I疊NjiMx)y{whj́Q&Fsu$yX3G{>yVVI8m$*:~0%dy2JO)(W7Jwۺ ic.Sp3ӊgXuܒ'~.NJ,ըfhM+R˶ '=zEg CWyž)sER]7#bQy ٳto|JwDa 0 /?Wd?u~+J׆FV{1xA$g_,9?I +g'MhSË"y;f4Vi>-Be@Xi) 3iO9<.ˆ 2Go9PND^ &a(3BR;QFv}+zYnѿE ]o`H*OղSI%jQxDk4:?e2 P n R$agQe6 { *)4Sq4n.fxy.m&ocYܸkGuH;]H6t6fMs~ᵔU$xi=Η)ڌo';'SrqCS Ykܔ9u##'9x8Qxe#jlw|WK/|CIJnI^abX1̗5%}Z F4GuwH?95tLf oodiu\7AFӳl#2 LqưƖwQwKND*n@zNO),}vWSj;jا7DY -? /jRPډndQ =W辣UjkN.r. rm=*,X ~Kg){Q|#K~8#@N _M5?=m4uG=ٓOX ,.rgQnjYdt3B7h\k|& ;Ɋp}gcYv=st,V|ts@B9j+d w2vFCF5~[d. }%`w{UwJfk."MUSIC>VF gqFi?aZXBm.S̷נ(q/&آcV:Tp;.G_/%*ZQow4|23teiSݤ`[z(P#u@Y)Py-]Ndq DK}}'5c/.D=&CwWpָՏG(^6SeޱK… z&ehs\x'CU{twXgDM\ʁ#+Sӻ J,ΒW^au QӖ/KKN QUdULrWv}.8͝VOݳP$-I>~ vojFq }FYN ^f=۸M73חpa(eTt='8֭E!El2|#,q#EeCr\i R[ xtK~ɅyE)*-V:r-lcxR :;0Q wv'֜> stream xڬcf[%Ҷm6mۮ]iۮT%+ι}ݿ=^f̈kɉTL%]x*Jƶf@9z;c_3;9+^՜in37^@K+W_ jZZ0Oߝ.@K{ws[G;s{׿UV 9@TQI[ZA@%47w6(Mr@Ss{sj3?S{3?0v\M{;8;]\.Kgc{׿=puMm!n/!Ggv}\\]LY$?]fn/_1jO.so`i-/__umotu1`cfonK=?"mo`f͝m?3CQoJ D Or-fk`lg&19?ml?HM v5 a{˿r010"47SZ,m_-@|jV@S.s{D2gTסj(E.Aq3spYX9f7!@7vuzto7u0gZT]46usvgow=MWLyC33]GtGC˛J3"CZfx>;9~cR_RnStq1#f^h,Bp0iO)Ct: u/ {rDnٍP"H->m^<,91_)^Fͦ &ݚױM{p; qݬ* DsT {6ܛ*P[4o/R^XvkփKY\Vzf}ƈer/gH>bҕvC䌚GLOA#淬M`mʃZ nYtsI$U e_ֻjYo/H2BE!JN}|4"wM4vHW 2BtP%jW([mso"ZkuY`06Θ@)AArL?CA<ĕ% Y"q?eQ5uf̥{b۶_ы>!$|n0{j4gh82JE*887H;Fh< D|tq:#U>uD!4PWUQhRq(*硉u+IKvΐQ=~.:d{:o!q`^c8:C΄lj8LMC,ʇJ=:r? !,R6XNepɖ5 M6/|md`{UVud{Q<=|HԎz{g?owH ⿸s,#$#^]z e})TB^Π>t>{ OmSJ]hP{F/ro~=UKVn@Ǒ-Q+ ߂l l*! Aq&sD-{#WN~ݍk qJ;KXPZ53'; Y3xSJק(su!Zl#q~ 4[Q5kqaڒQ6UQĹ~S?D0'v^x |1xv#%IqP2Y JzH&xYkߺ OG>#3u\f5)~#~K=k8wkpD{MH=eλz{VNF2WCBx,: WqOeliioXEJ<` acЖg]c^4Iyr;J7m\[ڏekM2B9*fLFVM˚#𣭿3@-@<,h A.S:mi2d\󟕦\Ԗ2Ƶv ߖ?E8:%wW==!iM j Q`U-cwi3 ׀F>bF-kq5@CjfYfT >XB6Iƨu^Pt0]DT|tc={dqm~w,LW^2Q_djFvm"Ė#y.n Ypz5^P'Foq¬ǿZ篛|A;Vz=118vw$m0BZ!IK=8`b 3#db)`=35K W!Gn(~6j-2M,0~sKO)fȽD' `:oh}qJݸo&0#Gh}rqN{~NU^":lOv29Oޚi8T3ndSSN='m˔­É\$!R6{8heʍJDT!|A?cpbkXp@W'ꂢC - CÀc$\܀8l|=2 G6Qc]e.>=Y#+?ylwm 3&&ĄU0iecOe vldaFш8u)!M$~`ҷf ^J0SY!ǨBAoݾ6#-GOY+0Mu 2jI6 $ ȣT$0-T"*6׀j_F^kiL\0,zTR.jc %MQ@C8M!?j#2q65quU_4+-SWֺ?pV\R."Ct~h? p|[|?qw[G;Qqɮam?oU*U3+HlgO &%" 6cF!\A ]zS' 2 \#=YKah и4Foи6І`C6o!rLIj~^0ěޥLLGwr#Il O} 6V i_Q A mR&RTFN:HNcw󴒌4JWJAtVkC9["Wh-[#>ff*Bº ;Vcf-eb nkBSB |0i*π`}n3SP,M @a%/U;; 8MJyqlH-gj=k/p.y< /Z:evduG!|nZ,ztt^Uʻݬ7Z.?Gwu9ujkii,0')O8hT(/ԶV!^) a"Te}j9&na*DPiPǛ.P hQYcO24mfvֹj/رJc37%>P)b+K~f\r4]g߰ME\adu k^.'+ns\hUl6h,T;Ս5E%r J@fU`6:eavR =֦ oچn8}p "T̼tLFӃUq$^ ^X6N?\$E&0f68ďV&apTEнTҿr=ډO7 6@JQ?kK dpO@bnQnJB~Ձمyu-IWgvtdҀ"0vK+ߌ(DR@V u$(Sbňi?ٱW{h%ɒB5xgW/kW+9.! > tEFIqRk~IAxʛ,O@kAܪ*}C$ZN)l k[يu¶e䒗-Ш{Oî)*5ĺ w\J4R8%>p)Gv.%A*{P@wX0pU14c9^_9{"6޸Je1PN0{-DOޗp.y3/hJ-jjB}IqIT$Xj[BkʤeX%yFfL'JWtZ^ >;. 4-+5#Ͼ$?4Tr\fnaF ӕ/ * `P+' xdBōCJ R)b#.EMV݂om+yزƠT1_沊@#\%됳j}2eg6<0] ži@*c}rހPFA dJr39@Lps۾c]F&[Mk#TTH 1jwLA`XB^ansHyЪ9cUh-BOQ|6NiE(Z/%e,"HϪSa,#<AkΦX pk"|0ycJiF1yy12c.bɈLſCom"؞C2zݛ49gP+ #0NkMTK$uMyJųVS;A;k|Ss(-s ёZ]D$%`qw4%H&咇;Tv؅x@S 78LYov7(fN}Kֈ`6Q.zMDÙAFhv~&^-Mmw o8*ؤktb=,i5u5 !a z bj=I BŖ]vIg:l`"29XM_zIxW28]?zo&&23yfr9kKeE|?+rE! n)È{?e$'ꨦõI}`܀󡯕C& #2#se X&b,ettHbQgGʦ`B\!Gjmߘ&bʎﲎml*ţdR#OTǷ)d1{yˁU5bRE]b8L XLYT_c,c>m׊e׶ȟwd:b;D7#|nڨUu6ms{ )1eِC|(ݚ mwkKڔx,wSf?5xZB t*@g}P“uPVv8G{P>PHYTo*Gtqs&֨$vΠ UvnI|~MjGeL5DfDMx3#B2Gq["uA'#J~Nʼn @ן؝~S.O[DP!$'&z#E5g*^#A"ۓgn=jjXMp>1w2de! CIT 6&[ɓ0Y@]06#vlK4ԉ%ajGħV?-7--za%_E*3$ =bx _Ӫ;ּ _H)fݷg6 jЄbA_6˲0(b;uȽߵ/: /Wj}3M-!mLɎaX0g_є7DKώ2؎#"4C2'c.uCVxSiGȇ/BBA5+@:"Oe5>d:&7F~(\sveO^M.=}luy*8i jwXG3zБ-7Y 1Rm vE~.5jhǖ+k]: AqIlf-8^xd&O]T0.8r&D`r!5:3"~x,>00FO or(8}PfK)ו"rŦo#z?ltmH;)tf(Pc:бtMU|DUE՗D&k~~<I㌍?5}֑Ѡ|0myC"?ɇÖ8cHssH% ˟Kw'Df"B=cCKAkm."vAWl_Ar?fÓ~%9q_[Rz ׅ VIī꧰8$L&(K *<`N &W`|BFS{gV%MGX4ڸZ;a,JNǸ m)h%T5H^Wo\)}7&&m[i~/4X4^ }wHQ$!}nK|:՗Bk6C ,ċz6NryZO"d]*"GV"3 љe͡;iMxEˣ^CdR 68DWD0GO)O0}s/G8GÊ)hDЕKlٚ'JjILfFh"gb͜zkbTՆxHߔaML&4뺼?S27mMC[ZA090J8{Cs^$M)Yg(޻p?M< >s'Ǯ+3+ղad,;9CWxH 2-K1⨞ h%j1C2t?ny=j }%rz)pG&Qze#3ƜXʦQ𰩙EOlsB(g\`R_\.;R(c˳f AS ؏7Zh!Zqw+H{ZBCs_; +At+o-Ĭ(~r4hw(2C[%1w7DJ"sl5w1 jmnr|vSIgN1n&KGÚO桔M{V "7WtrSbR84 [BߖP_*Pno^@[kp;;h8GP{'ŗ D©iLf?en|N~ع$O曘\]mŭd-eEbǘ.)R䃮M7Q%JoV%݆UL7a<9rokU8n}0+8U2_4Q"ҷ {Txגrq7V]axJiܵDMn0V149H69x2qϛHiM@M"oҰfZk i$ʾDhVlX P55gh7KiY'\TV!Z0HU%p`aږX`-xe%HㇽQUc,ck;a y4GJMXprqGCJcb3aDkstӑ+jwنf jc >\\ug ¯c(FiLd_qn="5s[t<"<_.C,\Ω&* Z2m䅧ϘƗindvMH^HϾTR>z50۬IFvJCͨjAmA'@ۺ٦ԁx}Rnw[)pvgyZNag6.K+$Qc,Q?P-?:R֊#n /%.r#~Nu cb=c0 Q-:Q'QC.&s=e;\<ޕAߊ爸:<],OZK#Gdž1Φ6ao{~%'0z\ːpiC֑x*- I{{]$?ӼO/T<7b'$+(%~λa)\Y38Q.h\SN"j^ <0UA &9G/Inj*/\N+Pzpt(V+v_vτMƺm\ŨtZB¢u̿H>N]Ee|Y#O`X=HݟcHGȐ'emas#5WNJ=\G=U "ʢnݍFQrR5K}Z['CZe͗#_hY> 8Z,Po¨K#TgĔNw\{8YQ>phIO|JӹdхF 0`[P\26嬕eQ,O?`Lml)cJ>|EbFGxړ0R 5 S3ڝ6_ARKS-ܭ9Ă$ǹ^6:G=v3}s_άOh9PO"#vf㇈ʦ6ޕHB\$\$Lv*n%x^9bĸ R2[~!sȠ6%xAJjt aO jK\qjÛ [[V#(U7A D1?E4f9ڈ+"yɜՉ7e@ Х[ibOb=yB3|d N"j hcH]D)<`ު"7z}X.VV%; rP88:ޥ`3?)S3 h/]JH@d}Ijݴi~Җ,ybUS~A7Zl؇^hV^Lr'}($7_3uytG'\|}U[‘-qk tsm7N 5l)EN] .ӡԢY}U"mrtR8W]C8fcjb;X@}Nʄ2Vc-T$,&tg=߂3F&. )QyvT\I܄0ٰU K{שЂp8M66^#n@uk&M }5N̩=9YSRB%o>Q֥`R)B [tL^4Ɇe >kW,@3߉bږg{oQ{FM\#{|7C=[=JO~=XlfkbCdܬ :.$+ Zk{5>Ķr%ZCu$M6Ddq)Sѷ|\nl~Z k|:*WBS败%~Xm4A|xݟx]\<'-b?-a[>At^O0*:-ݚЬw)q΃ѥ4qQy.&90l&D; hQ+Kt) ytٮN Qz:Nj+*. jyX{Nݙ luT|贆[%Vt\a2iOGeB V0{yXy(9.RK @43hژ5uYl\DSzw.cri*r- 8 I"-yI5rJt#A18 F14Z ^D镱BZ>qm[VDִzvv-,$=ţJEYkH8cۍ=7 V1!@!Y0lߴ*z O giEtR >~RKMh\7ml^&C}?fKu]gOkNf*kQ'ME_{$F7|j+dl ç92/r-HZ,`zbx>>x,q7rx -E@=xgg~.\6}ѵ/ ;ɭ%' \vİQW>hg-t殒t|OgRoG^=Amfz"̳UE֝;k}ӏ%. 6{^㙎 Ӯ\1ӽNJ:bP4ߛq?g3y 6ztP#h]D0R!oy)și?ޗ^?O`wLp.mNlA s,տ7 |zʘm\3y9#|kc,O-:>wX B'NV' O"`q'`;r!„yJGdYHU;X7+1^ n mLf &d4Q2˂e5iuY%+)Dg%|C$Q<ɪFňуn1gʈN$:i5]I4Ro51:KF\YC"|b ԫC,S!R5BуE^!7UMnz/Т *z?5쵿5 aڵ NΝp81ȩ Ci7ig;ғ4K,pi Ԟ8wƠZۗ:-*&L `5mu A6ȴ $:rlYӦ`Ҵ@lcݩJ8 ay ܽVgFz|rVV95Nv~X}<.~U7ay;rt]gGee]o=HFr h¢408.,`Y5n: ޝ_pץRMATyƗ;M+j0!*K'9 buf P\  Z8'Ip^1S9m}kATOSmp\4eeq֍}bPW,^WpkHK?uen;:Qbs./leCf:ɚĠ(NJKv t cD$ehV{nqUMwy9 BD$_NOCzw6ZﱔS2 0~ShVBQkf߰Y݊Fҟ\6,QݛUjV~푨eF)J45Zq /\ IۗblSy_qKc M~"ৰb2aa[ֳuHveL+=ZaۭXia<\l_yg`^zv})^ƞQQndWAFE7kmwD{wnC\K>T`_(ҼO=<\1f!)/;9cc|e?b֙- ?Jp&r)U2:ɞ]1t굛;`fr%Q砊 x-iM\~]=!@ɊF5 7]Uy󆁆/2^qYipqL&6xy*1ݑtwg!?AJūln/ϾlʘL@!‚ ՌQfjA=*vMbo3AA@D=3Xsl;l Qh+\8Pvc4.iTMNh(iq=N'Us[EHsojHu4pSy*ƩvkX|7V5k9?t#( 0z:Xo'a(/rDNpH :-RV=ّf}ռm!Pxbf]m*~ٴI6Y_4 %uJw݁vж"s3Tb`MAexə~$s;μ{S +ɜTB#v1`qeu%Fe\ln!xBuc'@w_RBVX59b~Tdprp_!/9ˆ%" :`T]239$I9jع@"ǀE #2f"dogyIeҨ  *h0qJJvX$)W<n;[Xj㒝_$'cW JR6ΑQl?n`5%bчB,~2+H&o })Mkts-97N$OնDZ,d*Q)њ㚡bFL/nV{ f *4 lM# RD/u3vbt/UoDN6c}R;. hsf^F\':U6e%לOp<<OW]`,a+h:,&7gCc2kT]q)W'*&2̄9%Lh⼼?Ψ8 ϜSyyŐ@-Nz>xozyK. duh XpkIHx$ZJ lTjtPt-Ih%/Y'/nYY}zW__\%S4n4m' hs kfRjV eZrw *b렿W$^ wݵdE"/RR h)3QҕS4h `ך1^y]ܝKK+zJCw7,ܑ+iCTݚ-<9~ Pɶ* j2k`9ȋT0,eˑlǐ%:9w9Jg^W ("_RX6_@+D.2&3XÅ_v%eE%d#v N0y<H碳,V0$͎M)t(Iz֙j -uph\F Ľ.qF!.v![{u lNU'D ׵nN0P?nmӮ\òa~zn[xjm8E"!?XAq9ve5.Y|ZSz0pm-wA۬,6ҥ闃de.|Z)!kZȾaFPԌ8VufFΓQTBԄEPt^vӄ=!q >s1(ﳪcItԎ)H5ܤd?J ˗k%εHiĬL垼=. 3QC2ڛncx59?RS.ÿg D>-mfbsk'4 з`Ua9h$~(ł$dzq tp\װkU ZqEkmk$;Yސ_IO0BΣޑ آ6ЪfY],rrx3V" aEEɤ`ƣ aSu%~h瑥0]9T gAj[-gy [Sy7!6;Or1#nSpWN\5P70dSKvE;~RdJrk8lO-KW9;1+Xܖ-:kS G $ ZwrOdUs`T@|~ma׺D1~XJ)`E'īHO/R{|< ?':X{T(%b?ڿIP) }J;Àʹ{@Uoï2X^]i&|:ikPw6B- TZW&‚c]a72 bg"nnS]:V=;$m) ^ˏs/ YW,WtKK~~׌t'΀F ODey[V'X\K=n}i?:W}4E>)T#iNaDaN PlWAHJd6KFI#Riy^AZV}Sl`crDCDj@\\ gK[/qPɤU.9e{Vpa%}f@D_,aډM ֣T+#}70Uo"X8?YV>`d8dBu YiVs*`AG%xȠ^~21R]`by.JV5M?G%ck<Ni/'~h20gwᄧ'kF`AC/3&~/rmvPQDЧ|DU4V]+VYȫah]~Ό* %Z$l^F;q@F&ȴV05vVzJv(C_)lL( $a $5ފzޏ[!4GQiZ|8?0m,2M fE̺eDZIAPa:&Z0r܈k N۶_.;s9]*墹Sy6hAW޹9¦mͼ$͜ d俑V] ӀP~[;H!(oі_7#e2F𒾚f*C׈^+O|!0̲G:L:xtχ->)ntֽȀA~d"v,!jf6G?3o2f9·vKHCm@QE p{Bhni8PZ9{@/lwTCb]GG+z҆ N_(][]d0Li7Ycfҥ!w"yy2UR#Z91`rUowqh`ߡrާC%Dr53a@A\ZB`%0uk:eԏwspZM@ЎljXfyg۲Mos0̕(ٔt}M'ŏ;&-YB`G6LwIi"&X+:$t a^4u೪TlDSxi-{J أ](Ԑ>CplͬH[rDRIXQ9m#`(.<ў[NWu29@ѕ=>I8rʷݲ ?b19ev% n;rf[`џ~žˬwr&ũG\Mysk8@P(1]xHwMs&3Q_K&P,uh^j^ Dٜ ڕS)ȑ7iRa}|wPx9ʪ@]4c=k8Ӏ&;8Ήxl/)j92C` S,-eV 8Dbc?)iM/zL,s\q`{w P+OUgx;?p'^ߓ)\X` v}} ,o倫r24xDo7VcϘEr\8,Gz9@ˮ3¾]!4/ŒB @kt f= D0FnA#؎"◁P74WÞ Pa d'Yۜo`K3 q?| {N1 9B3\;fRm65#QT]7zO~]߫G.@[," T5dC votBM:aoI wn^O4z2쭀P~o=kS̷[0UOŒR*nxZ s6:]:Hki7Mɩ r[bOhL|0V5ʝYm̫0M~:evAfA"VH!MR}蘵;_iY<{Ȯ;4d'ˉd`Q:W9OL@DX{CR% C@.-M1;HyMQzNJG3nmWCXIoXɄc.%A!'9Ц$cTj4lS^"Q%'Uf=5w!1;{8@- {̫"ܲf K;`S #Ut"w`Wo4s`H$u,!P)?'!p5\T1ˤ๿ll-F<֧ig+)n֊ܐVGK7ՍQ+=b Bj Co)14S< endstream endobj 163 0 obj << /Length1 1620 /Length2 7326 /Length3 0 /Length 8148 /Filter /FlateDecode >> stream xڭweT\ۖ5Np /4P@Nw ]Cpww#}~3^sd8*l0c )TP)&Af^Lzz){0H`!l \)=`|`baa o1bn`xpCa`#:P -3 z#, `UȂmM8C!&E 0-&0S$ -v1X`{k70g 6&PG<` =zX?bd*0=x̪"-Wp wn# =zL{yD ve Bl ܏d?e8:@l]+l7iO}K [[h;f\܏9M!6 o lg@ c S ` 6PSw*DH?"Mj_6ĐU"۲@aש}ۻMy歾v˶xQ'-l%&~-?8I0+(|Z[kjnў 7?ɇ)ۇ$2i3~5ayC]=m'_7)Y#1A^qԱpW#*{k'~aVDb}=727,2y->n7P0+r@6A oz͌tKgg`0E2Tg[Xȫ1p=mM},4d33'׭aB yx>auEAo_OZ< ;b#ZK;5z(8vxNrCOH| Tbދpz5:)GKK3pLQ01 /t,;깬1D*xe2iI#n1"T5OCĨCH)Td#k4Kf?a1,oK.DsCm [F-SI!0SbM6|T&nCӥe#V﬐+LMqqNU_^NZIL/#*aNp<_!'kWX)t\0.N)" كp-` "JjΈh>c F#Sy)x=,t~^toMw<ҼtvSWouݑy VU?fx͔6GX/>C}YkwlIWz4%A*hErM =¼&MBɊ6o9^jGK cw|i{۲oiSnpz#xnCG(Vȕj%~|>w?/ 'H¼NG@#?2>,[ҵYQӻ[g@k0/U>u+^ Sg48m}i ՘+?1 aK?W?InNh̐ܬDt[6nqKq/ Id{IG,2U㡒+SE&[=/a{zIot>ˋNZMZh1@b}+X>Fdͩm^IrlO8?ߐJcYp R2g`:eM)}W?9$bdOD7^:LHwXVV`6!QG۳E܏WqԚTdQG!jHDYY;>5'iEuqo3{ DJ}d7)4ޥXGnA6q^A'i:^bE`a%dNH6 uWoJF?X.iurg- qW7Gӹ Q /T픈[ d3P/g}꾛J7#Z5ũtC\[`aJ«")ښ7Gq2%Xc21, :\k0XVT8 ? E+5f׮9xZ8DxVx:ִ lnBGQuF&p e#>R|¦J2 \uE4mYC*(zsjF8ےJGҝiK X朦Kuwa#~"d8ʠs ;v4!w$n' EK<48YP.@1fob#:6^<ƹ:X4sjG!~Ve|{ⓡL+3&E]'QoG5r.,oڈR6l< < ^iV?푅Ds&*\&g[sX׋BzIAjӞAVFqpYNZ]Y.Ӈ&}iq/qzݙěs'ۻ3~P'u/M&$FN\Cя=.~o",㯰-h]9F?a 03ܼLH:x5y<-p%s Q7vS1YeK%٫_7/w/t:cK Ćh$YB U_O`t2c BԺfeBk<<>f9VnEo;btH$u"j@o}tR~^ǽ*gG`1k%pY!vuDNPgzc rn7jY+P"/&ܟI̦+A-wNJ\1y F#!b;a'?,,~,mԊuLsk\ܒE9 7fe.y܅Ne[ 9%l][ j| <\yӁaxv NXDL8\)&7K+gR/5-6hW$O<Û#J!t'ZW$`6^+ CO^"ӆ{h\N+; lLiΠZ~GnyIX7G"fM by&ɤ<$VSXZCdZAOAqK ӢhElznMJ#(_3OxKئ}amc>6&ˣgowgΡ^ʽ!˭ޝ\ Yr^.Yoߝym(qw,,8TwBH<_GOdbYNәm(4Dq8&;sM^:!*Yj]ܽIPKk2N@kwH,WEt|$K/_.t2}ǧۮ WvBOdGI7`"ż»R˧oyK<5t"9ML3FjںTPw}kes :טidTvc\QQ2'i9]9I'޼밫(nkΰMi=sd Zt#lΪ8'nݢeXb05o77Ѡwh2rQe"I=cCˁ#맺ۭ,zՑ|ѷ\ >ܤllhK?X]@sQf7"4Ƚu|?2?=̬NI,9fn@cD׀LQ^+6LmUL+F8&I#l&cI8Xl9|s LWۼ>F.#nZ/-%L()K-TLa{/eh |*2#Dv_vֹP5͑m]co~7C*F`T͓|hejFkC>2;#P~Þ>2T+1O͞eu0qsI|.QN Id=$ُYGq4*Ւh^H)󓵟qƝhŸz6IWY>}[Nzy 2h Րg3M{O~mOqfxg x&srwkqZoSyt3'4kX$"l{w1}3|9^-l(2 ~b-#e}W'T#p&a iF;/Yf ݗ8 Sr=1q 3,*|.( -hkRdśGgը@ ÏJY?MyTi5F="%Bs5.ƕ.D>%v q8{b?}ۗY; 80mi~Zip&G^PwS˓>R5I=KM;{[[>||%+ J_wduȐ "¨:/"?«NdU]6In \^? 3 + &ܢz_,?J5$m0{EV"+0w^b:׵\r.=3A6!Z7p. #cap `ԇV&@P)(B7@T&729AMc$ Ҭ=sp Z2RE7Vl&ۭ P<VlR`5PƵ{,WtT6ۋ=%IgRwWRSMj]~:7:_yTg"K  Ynn.YK ( C}ntuT~Դ > o^s{kng".\F>JT孫!#neaey] } q+$nٙ}Tނ3Ph!1%ɒ.l˚xp;!qؒAjG_#:Rh+zsJ4QVޗns^:) _dgÌź.%w30qOW/^lY%K:1=<@gpћdPX^ ZB*wz7l.8Z+|S[q:& O@ z|PV89ȉ6cȄ|"IF5aI1M:_EYR >A9ἢrY&ب6|H7L+fϥ?OyΔu CVH &vMkYbO۱5]d 1 X/VNqYyV]IB,8(@ZgM<)F7QP-}jR>i˥j0DSŢ[lr`GՁiD+R}b(W%{Ƨ!y",:;P&*U/}{[=t机O/'{j6N_ ;F{g :fܙ$MxRu[w š7b&ӫKo6u({~¤ D[-i %R#N* ^B=ɌĨEQ!|ӳz3Q>v›,=[;死Jˑ4=ވg;^H K'<ز TsIF+]u+kמ1o:7D_9;mȿZ$qm, &  .nuL>gKc9>IKZ>eŚ vZI{D+#@%<1R1B`X_O7[G*('%"Rv6y? ʼwY`69w=!@|nV7VTu?A+U"'0mPa4c4CYr) g\=6WdH/&N ~)]+~+Z/5y[z,|A>PI:}  f+ͤK?Mu-[%&[!=39Ow/1lg|YiolĆ8eX3oӗYڙP5MOu"  endstream endobj 187 0 obj << /Producer (pdfTeX-1.40.21) /Author()/Title()/Subject()/Creator(LaTeX with hyperref)/Keywords() /CreationDate (D:20201103203125Z) /ModDate (D:20201103203125Z) /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020/Arch Linux) kpathsea version 6.3.2) >> endobj 124 0 obj << /Type /ObjStm /N 68 /First 586 /Length 3406 /Filter /FlateDecode >> stream xZ[o~ϯ#V*-PK !64))Ϸdfy:B3rR&TiN2)& 4U0, )E,+M{E@o6X3ҏd!>gkfY# Ǭ3$Z0') VjÂ3#AZja0Y  h!PzO#`FjfؑPbm1@ E XK i0Vdd2HIjs (e*FG9(m1edT8=Jgh+>Y.&e>1fϚ}'88"Z\/'e-OI`fŞqx-iQrߎEsb.|V<nj?[]0*'t1ߓ;Dżdz٘rqɎ#{;7Dx\M'줂n^[o94~9͏i}'ܞ8?>7S®!)gjSF80;(bKwwh %=$ bt7XowC Mg!hXcDg_ʢτdwi9ch}eᘧ@ٸm}8ڸtn]ku9΢w'6b *:'U~('b!ŀx*ISr3J AJ X?X/*E>֘­Hv*i$h2ڱveJc  EGTB9٣ԡqCΆԠr,ERA Qy6#I]㈐Z&c 0)]*PD 0 *Y ؐ|y|UN*$꧋S~57zQ_f!\W4!8l\]˲b'i` 1K# O8+2Nji4R.*Ew|)ꘃ(z栋9`n0{Cd u*huѪؗ/>":@D[}ܥRc-%\OJ]:MOH&YH+Vd/ H÷{rxyvRT;1 #z Y>ûR,ka]/sQr)>>}yr|ob|:]{n!d( թrTW&bQ|}ULoPn0G1PJB3G9pnQ]]=&pۘ#NL}:@s5U쓊 @96ȣII+5@W(#oslM$+ CD"[@9" -y' ϚW'']5Sw-~\[;F Df%\A-{AheM\$ ~IBQ,}Ы-7nU]KB6M76e˦kJ;$rlFzdD蕘"ul[>oF : *OƷğ˪&'ǭd_OO*~k[4٨ ߶zsN'gJoQ7Æ頑?{K4a]vrQǶb0xՁ܁ h[rڦgUA#u>dtB;\jy^_3DӪA?F1~co4Tf9>6+mz_.pkj1W-4KR[j/{5Խ<(b{\KlȓΩڡ? ge\EUOUv1,ufX䝨_N/p! ] /Length 437 /Filter /FlateDecode >> stream xIZqn B(*222^=8bA ;<[SRٿ,1 crhpl\&ޣ }ULLՐxL=uˬƋhOnB4C B:9! I=8 Y^AaQqϛIx S0 =|e|)^TYJ\' `6_aa>@mPE8dhӭU|-v`}8C8c8E<^$QSAʸ%TO,z]DUzD4"nE9[#/|v<mu/\IZLyOnNcj.O1uzzvJDQ(BQ1D "A ,FFFFa<3Oj?JN_ endstream endobj startxref 240394 %%EOF phylonium-1.6/documentation/manual.tex000066400000000000000000000246151416332546600202760ustar00rootroot00000000000000\documentclass[a4paper,10pt,english]{scrartcl} \usepackage[utf8x]{inputenc} \usepackage{graphicx} \usepackage{hyperref} \usepackage{isodate} \usepackage{listings} \usepackage{xspace} \usepackage{mathpazo} \fontfamily{ppl} % \renewcommand{\rmdefault}{ppl} % rm \linespread{1.05} % Palatino needs more leadin \selectfont \usepackage[]{sourcesanspro} % \usepackage[]{sourceserifpro} % does not support italics :S \usepackage[]{sourcecodepro} \usepackage[T1]{fontenc} \title{Computing Phylogenies with Phylonium} \author{Fabian Klötzl\\\normalsize{kloetzl@evolbio.mpg.de}} \date{\isodate{\today}} \lstset{ columns=flexible, basicstyle={\ttfamily\small}, xleftmargin=2em, breaklines } \newcommand{\tool}[1]{\textsf{#1}} \newcommand{\phylonium}{\textsf{phylonium}\xspace} \begin{document} \frenchspacing \maketitle Biology textbooks usually contain a two part workflow to reconstruct the phylogenetic history of biological sequences: % \cite{fixme} First, align the sequences and then use a maximum likelihood approach to find the best tree. This method worked well for many years but has become less useful as the number of sequenced genomes grew rapidly and made existing workflows computationally unfeasible. To mitigate this problem so-called \emph{alignment-free} methods have been devised that quickly estimate phylogenies from unaligned sequences at reasonable accuracy. This document explains how to use one of these tools, \phylonium, for phylogeny reconstruction. \section{Installation from Source} \phylonium requires \tool{libdivsufsort} for fast suffix array computation. Install it via your package manager, for instance \lstinline!apt!. \begin{lstlisting} % sudo apt install libdivsufsort \end{lstlisting} At the moment \phylonium has to be build directly from the source code. This requires further dependencies. \begin{lstlisting} % sudo apt install libdivsufsort-dev build-essential automake \end{lstlisting} Get a local copy of \phylonium by cloning the repository: \begin{lstlisting}[language=bash] % git clone https://github.com/evolbioinf/phylonium \end{lstlisting} Change into the newly created directory and prepare the configuration of the package. \begin{lstlisting} % cd phylonium % autoreconf -fi \end{lstlisting} Now the directory contains a new file called \lstinline!configure!. This script will scan your system and configure the build procedure for \phylonium accordingly. \begin{lstlisting} % ./configure \end{lstlisting} \begin{quotation} \textbf{Note:} \phylonium uses x86 specific instructions to speed up the comparison of sequences. If you have a different CPU (e.\,g. arm), you should use \lstinline!./configure --disable-x86simd!. On old x86 systems with old compilers you may have to disable the optimizations for AVX512: \lstinline!./configure --disable-avx512!. \end{quotation} The configuration step fails if \tool{libdivsufsort} or the \tool{GSL} are not found. After successful configuration the program has to be compiled. \begin{lstlisting} % make \end{lstlisting} This step compiles the source code into the executable \lstinline!phylonium! located in the \lstinline!src! subdirectory. To make it available to all users do: \begin{lstlisting} % sudo make install \end{lstlisting} \section{Basic Usage} After \phylonium has been built, we can check whether it has been correctly installed. For that simply call \phylonium without any arguments. \begin{lstlisting} % phylonium Usage: phylonium [OPTIONS] FILES... FILES... can be any sequence of FASTA files, each file representing one genome. Options: -2, --2pass Enable two-pass algorithm -b, --bootstrap=N Print additional bootstrap matrices --complete-deletion Delete the whole aligned column in case of gaps -r FILE Set the reference genome -t, --threads=N The number of threads to be used; by default, all available processors are used -v, --verbose Print additional information -h, --help Display this help and exit --version Output version information and acknowledgments \end{lstlisting} \phylonium now prints the available command line arguments. The same can be achieved via \lstinline!phylonium -h!. In simplistic terms, \phylonium reads in assembled DNA sequences in FASTA format and produces a matrix of their evolutionary distances. It prints an error message if the input is malformed. All contigs belonging to the same genome are in the same input file. \phylonium uses the file name as identifier for the sequence. The final distance matrix is in PHYLIP format. To see how \phylonium works, we apply it to a set of 29~\textit{Escherichia coli} genomes. First, download and unpack them. \begin{lstlisting} % wget https://github.com/EvolBioInf/life2015/raw/gh-pages/eco29.fasta.gz % gunzip eco29.fasta.gz % cat eco29.fasta | awk -v RS='>' 'NR>1{print ">" $0 > $1 ".fa" }' \end{lstlisting} Now your working directory contains 29 individual genomic files with the extension \lstinline!.fa!. We begin by comparing only two of them. \begin{lstlisting} % phylonium FM180568.fa BA000007.fa 2 BA000007 0.0000e+00 2.4833e-02 FM180568 2.4833e-02 0.0000e+00 \end{lstlisting} On the first line of the output is the number of genomes compared followed by a two-by-two matrix. Each value represents an estimated substitution rate between two sequences. As each genome is equal to itself the main diagonal consists only of zeros. So the evolutionary distance between sequences \lstinline!BA000007! (first row) and \lstinline!FM180568! (second column) is $2.4833 \cdot{10}^{-2}$ substitutions per site. Note that the matrix is symmetric. Next we execute \phylonium on the whole dataset. \begin{lstlisting} % phylonium *.fa > eco29.out \end{lstlisting} Now the file \lstinline!eco29.out! contains a distance matrix of size 29 by 29, which can be summarized as a phylogeny using the neighbor-joining algorithm implemented in, for example, my \tool{mat tools}\footnote{\url{https://github.com/kloetzl/mattools}}. \begin{lstlisting} % mat nj eco29.out > eco29.tree % cat eco29.tree ((((((((AE005174:-0.000019,BA000007:1.8692e-04)100:0.000738,CP001846:7.8206e-04)100:0.005264,CP000034:7.1481e-03)100:0.002179,((((((AE014075:0.003806,CU928162:4.0810e-03)30:0.000106,((CP000243:0.000287,CP000468:2.8884e-04)92:0.000463,CU928161:6.3318e-04)100:3.3526e-03)84:0.000290,CP000247:4.3564e-03)100:0.001367,FM180568:5.7900e-03)100:0.006160,(CU928164:0.006474,CP000970:6.6612e-03)100:2.9209e-03)100:0.001376,CU928163:9.6094e-03)100:3.7308e-03)100:0.001825,((((AP009048:0.000021,U00096:-1.2523e-05)100:0.000054,CP001396:5.3529e-05)42:0.000005,CP000948:-1.0393e-05)100:0.004144,(CP000802:0.002662,CP000946:3.0385e-03)100:8.4870e-04)100:1.8957e-03)60:0.000689,((AE005674:0.000240,AE014073:7.9602e-05)100:0.000401,CP000266:5.1495e-04)100:6.4055e-03)78:0.000581,((AP009240:0.003100,CU928160:2.7861e-03)100:0.000288,CP000800:3.2921e-03)100:1.9029e-03)89:0.000362,CP000038:4.6574e-03,(CP001063:0.001312,CP000036:1.3202e-03)100:3.6383e-03); \end{lstlisting} This phylogeny in Newick format, which I am here visualizing using the ETE Toolkit\footnote{\url{http://etetoolkit.org/}}. \begin{figure} \begin{center} \includegraphics[width=0.9\linewidth]{eco29.pdf} \end{center} \caption{A phylogeny of 29 \textit{Escherichia coli} and \textit{Shigella} genomes; This phylogeny was computed using \tool{phylonium} default arguments, neighbor joining and then visualized with ETE.} \end{figure} \section{Picking a Reference} Internally, \phylonium \emph{aligns} all sequences to a reference. This simplified alignment is then used to estimate the rate of substitutions on homologous regions. The reference can impact the accuracy of the phylogeny. With the \lstinline!--verbose! switch \phylonium prints the reference it has picked automatically. \begin{lstlisting} % phylonium *.fa --verbose chosen reference: AP009240 ref: AP009240 Mapping 29 sequences: 100.0% (29/29), done. Comparing the sequences: 100.0% (406/406), done. ... avg coverage: 0.738057 alignment: 110170145 137065486 0.803777 \end{lstlisting} It may be beneficial to explicitly set the reference, for instance if the data set contains an assembly of the type strain. This can is done with the \lstinline!-r! argument. \begin{lstlisting} % phylonium *.fa --verbose -r FM180568.fa ref: FM180568 ... \end{lstlisting} For as yet unexplored data sets it may be hard to choose a good reference. For these cases \phylonium comes with the \lstinline!--2pass! option. If enabled, after a first run, \phylonium picks a central sequence as a reference, which is a reasonable choice for most data sets. \begin{lstlisting} % phylonium *.fa --verbose --2pass chosen reference: AP009240 ref: AP009240 ref: CP000948 ... \end{lstlisting} \section{Dealing with Gaps} \phylonium estimated the evolutionary distance as the number of substitutions per site. However, substitutions are only one type of mutations. Indels frequently disturb the homology of sequences. There exist two standard methods to handle the corresponding gaps: pairwise and complete deletion. Consider the following alignment. \begin{lstlisting}[columns=fixed] Seq1 AACTT Seq2 AGGTT Seq3 AG-TC \end{lstlisting} Under pairwise deletion (the default in \phylonium) \lstinline!Seq1! is separated by two mismatches both from \lstinline!Seq2! and \lstinline!Seq3!; the difference being that there are five homologous nucleotides between \lstinline!Seq1! and \lstinline!Seq2!, whereas \lstinline!Seq1! and \lstinline!Seq3! share only four nucleotides. So the pairwise deletion model only considers gaps in the two sequences under consideration, hence the name. With complete deletion, this changes. Whenever there is a gap in the data, the whole column is masked. Thus, the above dataset becomes transformed into the following. \begin{lstlisting}[columns=fixed] Seq1 AA-TT Seq2 AG-TT Seq3 AG-TC \end{lstlisting} The distance between \lstinline!Seq1! and \lstinline!Seq3! remains unchanged; But the distance from \lstinline!Seq! to \lstinline!Seq2! changes from $\frac{2}{5}$ to $\frac{1}{4}$. Activating the \lstinline!--complete-deletion! option can be advantageous when analyzing data sets with divergent sequences. However, complete deletion biases the estimation towards conserved genetic regions. The resulting distances are often too small. On the other hand, complete deletion can result in the loss of a large part of the input data, for example, if a single assembly, out of potentially hundreds, failed. So use complete deletion with caution. \end{document} phylonium-1.6/libs/000077500000000000000000000000001416332546600143475ustar00rootroot00000000000000phylonium-1.6/libs/Makefile.am000066400000000000000000000023531416332546600164060ustar00rootroot00000000000000noinst_LIBRARIES= \ libcompat.a \ libpfasta.a \ librevseqcmp.a \ libseqcmp.a libpfasta_a_SOURCES= pfasta.c pfasta.h libpfasta_a_CPPFLAGS= -I$(top_srcdir)/opt libcompat_a_SOURCES = compat-stdlib.h if !HAVE_REALLOCARRAY libcompat_a_SOURCES+= reallocarray.c endif libseqcmp_a_SOURCES = seqcmp.c seqcmp.h if ENABLE_X86_SIMD noinst_LIBRARIES+= libseqcmp_sse2.a libseqcmp_avx2.a libseqcmp_a_LIBADD = libseqcmp_sse2.a libseqcmp_avx2.a libseqcmp_sse2_a_SOURCES = seqcmp_sse2.c libseqcmp_sse2_a_CFLAGS = -mpopcnt -msse2 libseqcmp_avx2_a_SOURCES = seqcmp_avx2.c libseqcmp_avx2_a_CFLAGS = -mpopcnt -mavx2 if ENABLE_AVX512 noinst_LIBRARIES+=libseqcmp_avx512.a libseqcmp_a_LIBADD+=libseqcmp_avx512.a libseqcmp_avx512_a_SOURCES = seqcmp_avx512.c libseqcmp_avx512_a_CFLAGS = -mpopcnt -mavx512bw -mavx512vl endif endif librevseqcmp_a_SOURCES = revseqcmp.c revseqcmp.h librevseqcmp_a_LIBADD = if ENABLE_X86_SIMD noinst_LIBRARIES+= librevseqcmp_ssse3.a librevseqcmp_avx2.a librevseqcmp_a_LIBADD+= librevseqcmp_ssse3.a librevseqcmp_avx2.a librevseqcmp_ssse3_a_SOURCES = revseqcmp_ssse3.c librevseqcmp_ssse3_a_CFLAGS = -mpopcnt -mssse3 librevseqcmp_avx2_a_SOURCES = revseqcmp_avx2.c librevseqcmp_avx2_a_CFLAGS = -mpopcnt -mavx2 endif format: clang-format -i *.h *.c phylonium-1.6/libs/compat-stdlib.h000066400000000000000000000002601416332546600172600ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ #include void *reallocarray(void *optr, size_t nmemb, size_t size); phylonium-1.6/libs/pfasta.c000066400000000000000000000402471416332546600160000ustar00rootroot00000000000000/* * Copyright (c) 2015-2020, Fabian Klötzl * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #include "pfasta.h" #define VERSION "v15" #ifdef __SSE2__ #include #endif #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) #include #define PFASTA_THREADSAFE 1 #else #define thread_local #define PFASTA_THREADSAFE 0 #endif int pfasta_threadsafe() { return PFASTA_THREADSAFE ; } /** The following is the maximum length of an error string. It has to be * carefully chosen, so that all calls to PF_FAIL_STR succeed. For instance, * the line number can account for up to 20 characters. */ #define PF_ERROR_STRING_LENGTH 100 thread_local char errstr_buffer[PF_ERROR_STRING_LENGTH]; void *pfasta_reallocarray(void *ptr, size_t nmemb, size_t size); #define BUFFER_SIZE 16384 #define LIKELY(X) __builtin_expect((intptr_t)(X), 1) #define UNLIKELY(X) __builtin_expect((intptr_t)(X), 0) enum { NO_ERROR, E_EOF, E_ERROR, E_ERRNO, E_BUBBLE, E_STR, E_STR_CONST }; #define PF_FAIL_ERRNO(PP) \ do { \ (void)strerror_r(errno, errstr_buffer, PF_ERROR_STRING_LENGTH); \ (PP)->errstr = errstr_buffer; \ return_code = E_ERRNO; \ goto cleanup; \ } while (0) #define PF_FAIL_BUBBLE_CHECK(PP, CHECK) \ do { \ if (UNLIKELY(CHECK)) { \ return_code = CHECK; \ goto cleanup; \ } \ } while (0) #define PF_FAIL_BUBBLE(PP) \ do { \ if (UNLIKELY((PP)->errstr)) { \ return_code = E_BUBBLE; \ goto cleanup; \ } \ } while (0) #define PF_FAIL_STR_CONST(PP, STR) \ do { \ (PP)->errstr = (STR); \ return_code = E_STR_CONST; \ goto cleanup; \ } while (0) #define PF_FAIL_STR(PP, ...) \ do { \ (void)snprintf(errstr_buffer, PF_ERROR_STRING_LENGTH, __VA_ARGS__); \ (PP)->errstr = errstr_buffer; \ return_code = E_STR; \ goto cleanup; \ } while (0) int pfasta_read_name(struct pfasta_parser *pp, struct pfasta_record *pr); int pfasta_read_comment(struct pfasta_parser *pp, struct pfasta_record *pr); int pfasta_read_sequence(struct pfasta_parser *pp, struct pfasta_record *pr); static inline char *buffer_begin(struct pfasta_parser *pp); static inline char *buffer_end(struct pfasta_parser *pp); static inline int buffer_advance(struct pfasta_parser *pp, size_t steps); static inline int buffer_is_empty(const struct pfasta_parser *pp); static inline int buffer_is_eof(const struct pfasta_parser *pp); static inline int buffer_peek(struct pfasta_parser *pp); static inline int buffer_read(struct pfasta_parser *pp); typedef struct dynstr { char *str; size_t capacity, count; } dynstr; static inline char *dynstr_move(dynstr *ds); static inline int dynstr_init(dynstr *ds, struct pfasta_parser *pp); static inline size_t dynstr_len(const dynstr *ds); static inline void dynstr_free(dynstr *ds); static inline int dynstr_append(dynstr *ds, const char *str, size_t length, struct pfasta_parser *pp); static inline int my_isspace(int c) { // ascii whitespace return (c >= '\t' && c <= '\r') || (c == ' '); } const char *pfasta_version(void) { return VERSION; } int buffer_init(struct pfasta_parser *pp) { int return_code = 0; pp->buffer = malloc(BUFFER_SIZE); if (!pp->buffer) PF_FAIL_ERRNO(pp); int check = buffer_read(pp); PF_FAIL_BUBBLE_CHECK(pp, check); cleanup: return return_code; } int buffer_read(struct pfasta_parser *pp) { int return_code = NO_ERROR; ssize_t count = read(pp->file_descriptor, pp->buffer, BUFFER_SIZE); if (UNLIKELY(count < 0)) PF_FAIL_ERRNO(pp); if (UNLIKELY(count == 0)) { // EOF pp->fill_ptr = pp->buffer; pp->read_ptr = pp->buffer + 1; pp->errstr = "EOF (maybe error)"; // enable bubbling return E_EOF; } pp->read_ptr = pp->buffer; pp->fill_ptr = pp->buffer + count; cleanup: return return_code; } int buffer_peek(struct pfasta_parser *pp) { return LIKELY(pp->read_ptr < pp->fill_ptr) ? *(unsigned char *)pp->read_ptr : EOF; } char *buffer_begin(struct pfasta_parser *pp) { return pp->read_ptr; } char *buffer_end(struct pfasta_parser *pp) { return pp->fill_ptr; } inline int buffer_advance(struct pfasta_parser *pp, size_t steps) { int return_code = 0; pp->read_ptr += steps; if (UNLIKELY(pp->read_ptr >= pp->fill_ptr)) { assert(pp->read_ptr == pp->fill_ptr); int check = buffer_read(pp); // resets pointers PF_FAIL_BUBBLE_CHECK(pp, check); } cleanup: return return_code; } int buffer_is_empty(const struct pfasta_parser *pp) { return pp->read_ptr == pp->fill_ptr; } int buffer_is_eof(const struct pfasta_parser *pp) { return pp->read_ptr > pp->fill_ptr; } char *find_first_space(const char *begin, const char *end) { size_t offset = 0; size_t length = end - begin; #ifdef __SSE2__ typedef __m128i vec_type; static const size_t vec_size = sizeof(vec_type); const vec_type all_tab = _mm_set1_epi8('\t' - 1); const vec_type all_carriage = _mm_set1_epi8('\r' + 1); const vec_type all_space = _mm_set1_epi8(' '); size_t vec_offset = 0; size_t vec_length = (end - begin) / vec_size; for (; vec_offset < vec_length; vec_offset++) { vec_type chunk; memcpy(&chunk, begin + vec_offset * vec_size, vec_size); // isspace: \t <= char <= \r || char == space vec_type v1 = _mm_cmplt_epi8(all_tab, chunk); vec_type v2 = _mm_cmplt_epi8(chunk, all_carriage); vec_type v3 = _mm_cmpeq_epi8(chunk, all_space); unsigned int vmask = (_mm_movemask_epi8(v1) & _mm_movemask_epi8(v2)) | _mm_movemask_epi8(v3); if (UNLIKELY(vmask)) { offset += __builtin_ctz(vmask); offset += vec_offset * vec_size; return (char *)begin + offset; } } offset += vec_offset * vec_size; #endif for (; offset < length; offset++) { if (my_isspace(begin[offset])) break; } return (char *)begin + offset; } char *find_first_not_space(const char *begin, const char *end) { size_t offset = 0; size_t length = end - begin; for (; offset < length; offset++) { if (!my_isspace(begin[offset])) break; } return (char *)begin + offset; } size_t count_newlines(const char *begin, const char *end) { size_t offset = 0; size_t length = end - begin; size_t newlines = 0; for (; offset < length; offset++) { if (begin[offset] == '\n') newlines++; } return newlines; } static int copy_word(struct pfasta_parser *pp, dynstr *target) { int return_code = 0; int c; while (c = buffer_peek(pp), c != EOF && LIKELY(!my_isspace(c))) { char *end_of_word = find_first_space(buffer_begin(pp), buffer_end(pp)); size_t word_length = end_of_word - buffer_begin(pp); assert(word_length > 0); int check = dynstr_append(target, buffer_begin(pp), word_length, pp); PF_FAIL_BUBBLE_CHECK(pp, check); check = buffer_advance(pp, word_length); PF_FAIL_BUBBLE_CHECK(pp, check); } cleanup: return return_code; } static int skip_whitespace(struct pfasta_parser *pp) { int return_code = 0; while (my_isspace(buffer_peek(pp))) { char *split = find_first_not_space(buffer_begin(pp), buffer_end(pp)); // advance may clear the buffer. So count first … size_t newlines = count_newlines(buffer_begin(pp), split); int check = buffer_advance(pp, split - buffer_begin(pp)); PF_FAIL_BUBBLE_CHECK(pp, check); // … and then increase the counter. pp->line_number += newlines; } cleanup: return return_code; } struct pfasta_parser pfasta_init(int file_descriptor) { int return_code = 0; struct pfasta_parser pp = {0}; pp.line_number = 1; pp.file_descriptor = file_descriptor; int check = buffer_init(&pp); if (check && check != E_EOF) PF_FAIL_BUBBLE_CHECK(&pp, check); if (buffer_is_empty(&pp) || buffer_is_eof(&pp)) { PF_FAIL_STR(&pp, "File is empty."); } if (buffer_peek(&pp) != '>') { PF_FAIL_STR(&pp, "File must start with '>'."); } cleanup: // free buffer if necessary if (return_code) { pfasta_free(&pp); } pp.done = return_code || buffer_is_eof(&pp); return pp; } struct pfasta_record pfasta_read(struct pfasta_parser *pp) { int return_code = 0; struct pfasta_record pr = {0}; int check = pfasta_read_name(pp, &pr); PF_FAIL_BUBBLE_CHECK(pp, check); check = pfasta_read_comment(pp, &pr); PF_FAIL_BUBBLE_CHECK(pp, check); check = pfasta_read_sequence(pp, &pr); PF_FAIL_BUBBLE_CHECK(pp, check); cleanup: if (return_code) { pfasta_record_free(&pr); pfasta_free(pp); } pp->done = return_code || buffer_is_eof(pp); return pr; } int pfasta_read_name(struct pfasta_parser *pp, struct pfasta_record *pr) { int return_code = 0; dynstr name; dynstr_init(&name, pp); PF_FAIL_BUBBLE(pp); assert(!buffer_is_empty(pp)); if (buffer_peek(pp) != '>') { PF_FAIL_STR(pp, "Expected '>' but found '%c' on line %zu.", buffer_peek(pp), pp->line_number); } int check = buffer_advance(pp, 1); // skip > if (check == E_EOF) PF_FAIL_STR(pp, "Unexpected EOF in name on line %zu.", pp->line_number); PF_FAIL_BUBBLE(pp); check = copy_word(pp, &name); if (check == E_EOF) PF_FAIL_STR(pp, "Unexpected EOF in name on line %zu.", pp->line_number); PF_FAIL_BUBBLE(pp); if (dynstr_len(&name) == 0) PF_FAIL_STR(pp, "Empty name on line %zu.", pp->line_number); pr->name_length = dynstr_len(&name); pr->name = dynstr_move(&name); cleanup: if (return_code) { dynstr_free(&name); } return return_code; } int pfasta_read_comment(struct pfasta_parser *pp, struct pfasta_record *pr) { int return_code = 0; if (buffer_peek(pp) == '\n') { pr->comment_length = 0; pr->comment = NULL; return 0; } dynstr comment; dynstr_init(&comment, pp); PF_FAIL_BUBBLE(pp); assert(!buffer_is_empty(pp)); int check = buffer_advance(pp, 1); // skip first whitespace if (check == E_EOF) goto label_eof; PF_FAIL_BUBBLE(pp); assert(!buffer_is_empty(pp)); // get comment while (buffer_peek(pp) != '\n') { check = dynstr_append(&comment, buffer_begin(pp), 1, pp); PF_FAIL_BUBBLE_CHECK(pp, check); check = buffer_advance(pp, 1); if (check == E_EOF) goto label_eof; PF_FAIL_BUBBLE_CHECK(pp, check); } label_eof: if (buffer_is_eof(pp)) PF_FAIL_STR(pp, "Unexpected EOF in comment on line %zu.", pp->line_number); pr->comment_length = dynstr_len(&comment); pr->comment = dynstr_move(&comment); cleanup: if (return_code) { dynstr_free(&comment); } return return_code; } int pfasta_read_sequence(struct pfasta_parser *pp, struct pfasta_record *pr) { int return_code = 0; dynstr sequence; dynstr_init(&sequence, pp); PF_FAIL_BUBBLE(pp); assert(!buffer_is_empty(pp)); assert(!buffer_is_eof(pp)); assert(buffer_peek(pp) == '\n'); int check = skip_whitespace(pp); if (check == E_EOF) PF_FAIL_STR(pp, "Empty sequence on line %zu.", pp->line_number); PF_FAIL_BUBBLE_CHECK(pp, check); // Assume a line begins only with alpha, -, *, or more spaces char c; while (c = buffer_peek(pp), LIKELY(isalpha(c) || c == '-' || c == '*')) { int check = copy_word(pp, &sequence); if (UNLIKELY(check == E_EOF)) break; PF_FAIL_BUBBLE_CHECK(pp, check); // optimize for more common case ptrdiff_t length = buffer_end(pp) - buffer_begin(pp); if (LIKELY(length >= 2 && buffer_begin(pp)[0] == '\n' && buffer_begin(pp)[1] > ' ')) { pp->read_ptr++; // nasty hack pp->line_number += 1; } else { check = skip_whitespace(pp); if (UNLIKELY(check == E_EOF)) break; PF_FAIL_BUBBLE_CHECK(pp, check); } } if (dynstr_len(&sequence) == 0) PF_FAIL_STR(pp, "Empty sequence on line %zu.", pp->line_number); pr->sequence_length = dynstr_len(&sequence); pr->sequence = dynstr_move(&sequence); pp->errstr = NULL; // reset error cleanup: if (return_code) { dynstr_free(&sequence); } return return_code; } void pfasta_record_free(struct pfasta_record *pr) { if (!pr) return; free(pr->name); free(pr->comment); free(pr->sequence); pr->name = pr->comment = pr->sequence = NULL; } void pfasta_free(struct pfasta_parser *pp) { if (!pp) return; free(pp->buffer); pp->buffer = NULL; } /** @brief Creates a new string that can grow dynamically. * * @param ds - A reference to the dynstr container. * * @returns 0 iff successful. */ static inline int dynstr_init(dynstr *ds, struct pfasta_parser *pp) { int return_code = 0; *ds = (dynstr){NULL, 0, 0}; ds->str = malloc(61); if (!ds->str) PF_FAIL_ERRNO(pp); ds->str[0] = '\0'; ds->capacity = 61; ds->count = 0; cleanup: return return_code; } /** @brief A append more than one character to a string. * * @param ds - A reference to the dynstr container. * @param str - The new characters. * @param length - number of new characters to append * * @returns 0 iff successful. */ static inline int dynstr_append(dynstr *ds, const char *str, size_t length, struct pfasta_parser *pp) { int return_code = 0; size_t required = ds->count + length; if (UNLIKELY(required >= ds->capacity)) { char *neu = pfasta_reallocarray(ds->str, required / 2, 3); if (UNLIKELY(!neu)) { dynstr_free(ds); PF_FAIL_ERRNO(pp); } ds->str = neu; ds->capacity = (required / 2) * 3; } memcpy(ds->str + ds->count, str, length); ds->count = required; cleanup: return return_code; } /** @brief Frees a dynamic string. */ static inline void dynstr_free(dynstr *ds) { if (!ds) return; free(ds->str); *ds = (dynstr){NULL, 0, 0}; } /** @brief Returns the string as a standard `char*`. The internal reference is * then deleted. Hence the name *move* as in *move semantics*. * * @param ds - The dynamic string to move from. * * @returns a `char*` to a standard null-terminated string. */ static inline char *dynstr_move(dynstr *ds) { char *out = pfasta_reallocarray(ds->str, ds->count + 1, 1); if (!out) { out = ds->str; } out[ds->count] = '\0'; *ds = (dynstr){NULL, 0, 0}; return out; } /** @brief Returns the current length of the dynamic string. */ static inline size_t dynstr_len(const dynstr *ds) { return ds->count; } __attribute__((weak)) void *reallocarray(void *ptr, size_t nmemb, size_t size); /** * @brief Unsafe fallback in case reallocarray isn't provided by the stdlib. */ void *pfasta_reallocarray(void *ptr, size_t nmemb, size_t size) { if (reallocarray == NULL) { return realloc(ptr, nmemb * size); } else { return reallocarray(ptr, nmemb, size); } } phylonium-1.6/libs/pfasta.h000066400000000000000000000053631416332546600160050ustar00rootroot00000000000000/* * Copyright (c) 2015-2020, Fabian Klötzl * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #ifndef PFASTA_H #define PFASTA_H #ifdef __cplusplus extern "C" { #endif #include /** * There is no magic to this structure. Its just a container of three strings. * Feel free to duplicate or move them. But don't forget to free the data after * usage! */ struct pfasta_record { char *name, *comment, *sequence; size_t name_length, comment_length, sequence_length; }; /** * This structure holds a number of members to represent the state of the FASTA * parser. Please make sure that it is properly initialized before usage. * Always free this structure when the parser is done. */ struct pfasta_parser { const char *errstr; int done; /*< private -- do not touch! >*/ int file_descriptor; char *buffer; char *read_ptr, *fill_ptr; size_t line_number; }; /** * This function initializes a `pfasta_parser` struct with a parser bound to a * specific file descriptor. Iff an error occurred `errstr` is set to contain a * suitable message. Otherwise you can read data from it as long as `done` isn't * set. The parser should be freed after usage. * * Please note that the user is responsible for opening the file descriptor as * readable and closing after usage. */ struct pfasta_parser pfasta_init(int file_descriptor); /** * Using a properly initialized parser, this function can read FASTA sequences. * These are stored in the simple structure and returned. On error, the `errstr` * property of the parser is set. */ struct pfasta_record pfasta_read(struct pfasta_parser *pp); /** * This function frees the resources held by a pfasta record. */ void pfasta_record_free(struct pfasta_record *pr); /** * This function frees the resources held by a pfasta parser. */ void pfasta_free(struct pfasta_parser *pp); /** * Get a string defining the version of the pfasta library. */ const char *pfasta_version(void); /** * Returns 0 iff pfasta is not threadsafe. */ int pfasta_threadsafe(); #ifdef __cplusplus } #endif #endif /* PFASTA_H */ phylonium-1.6/libs/reallocarray.c000066400000000000000000000023671416332546600172030ustar00rootroot00000000000000#include #include #include "compat-stdlib.h" /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void *reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } phylonium-1.6/libs/revseqcmp.c000066400000000000000000000030131416332546600165150ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2019 (C) Fabian Klötzl */ #include "revseqcmp.h" #include "config.h" #include #define UNLIKELY(X) __builtin_expect((X), 0) int is_complement(char c, char d); size_t revseqcmp_generic(const char *begin, const char *other, size_t length) { assert(begin != NULL); assert(other != NULL); size_t substitutions = 0; size_t i = 0; for (; i < length; i++) { if (UNLIKELY(!is_complement(begin[i], other[length - 1 - i]))) { substitutions++; } } return substitutions; } #ifdef ENABLE_X86_SIMD revseqcmp_fn *revseqcmp_select(void) { // As ifunc resolvers are called before any constructors run, we explicitly // have to initialize the cpu model detector. // https://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html __builtin_cpu_init(); if (__builtin_cpu_supports("popcnt") && __builtin_cpu_supports("avx2")) { return revseqcmp_avx2; } else if (__builtin_cpu_supports("popcnt") && __builtin_cpu_supports("ssse3")) { return revseqcmp_ssse3; } else { return revseqcmp_generic; } } #if __has_attribute(ifunc) && HAVE_FUNC_ATTRIBUTE_IFUNC size_t revseqcmp(const char *begin, const char *other, size_t length) __attribute__((ifunc("revseqcmp_select"))); #else size_t revseqcmp(const char *begin, const char *other, size_t length) { return (revseqcmp_select())(begin, other, length); } #endif #else size_t revseqcmp(const char *begin, const char *other, size_t length) { return revseqcmp_generic(begin, other, length); } #endif phylonium-1.6/libs/revseqcmp.h000066400000000000000000000015251416332546600165300ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2019 © Fabian Klötzl */ #pragma once #ifdef __cplusplus extern "C" { #endif #include #include "config.h" /** @brief Check whether two characters are complementary. * @param c - One nucleotide. * @param d - A nucleotide from the other sequence. * @returns true iff the two nucleotides are complements. */ inline int is_complement(char c, char d) { int xorr = c ^ d; return (xorr & 6) == 4; } size_t revseqcmp(const char *begin, const char *other, size_t length); #ifdef ENABLE_X86_SIMD size_t revseqcmp_ssse3(const char *begin, const char *other, size_t length); size_t revseqcmp_avx2(const char *begin, const char *other, size_t length); #endif typedef size_t(revseqcmp_fn)(const char *begin, const char *other, size_t length); #ifdef __cplusplus } #endif phylonium-1.6/libs/revseqcmp_avx2.c000066400000000000000000000027511416332546600174650ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2019 © Fabian Klötzl */ #include #include #include #include #include #include "revseqcmp.h" size_t revseqcmp_avx2(const char *self, const char *other, size_t length) { size_t substitutions = 0; size_t offset = 0; typedef __m256i vec_type; size_t vec_size = sizeof(vec_type); size_t vec_offset = 0; size_t vec_length = length / vec_size; substitutions += vec_size * vec_length; vec_type mask = _mm256_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); vec_type mask6 = _mm256_set1_epi8(6); vec_type mask4 = _mm256_set1_epi8(4); for (; vec_offset < vec_length; vec_offset++) { vec_type b; memcpy(&b, self + vec_offset * vec_size, vec_size); vec_type o; size_t pos = length - (vec_offset + 1) * vec_size; memcpy(&o, other + pos, vec_size); vec_type reversed = _mm256_shuffle_epi8(o, mask); vec_type swapped = _mm256_permute2x128_si256(b, b, 1); vec_type v1 = _mm256_xor_si256(swapped, reversed); vec_type v2 = _mm256_and_si256(v1, mask6); vec_type v3 = _mm256_cmpeq_epi8(v2, mask4); unsigned int vmask = _mm256_movemask_epi8(v3); substitutions -= __builtin_popcount(vmask); } offset += vec_offset * vec_size; for (; offset < length; offset++) { if (!is_complement(self[offset], other[length - 1 - offset])) { substitutions++; } } return substitutions; } phylonium-1.6/libs/revseqcmp_ssse3.c000066400000000000000000000025411416332546600176420ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2019 © Fabian Klötzl */ #include #include #include #include #include "revseqcmp.h" size_t revseqcmp_ssse3(const char *self, const char *other, size_t length) { size_t substitutions = 0; size_t offset = 0; size_t vec_offset = 0; size_t vec_length = length / sizeof(__m128i); substitutions += sizeof(__m128i) * vec_length; for (; vec_offset < vec_length; vec_offset++) { __m128i b; memcpy(&b, self + vec_offset * sizeof(__m128i), sizeof(b)); __m128i o; size_t pos = length - (vec_offset + 1) * sizeof(__m128i); memcpy(&o, other + pos, sizeof(o)); __m128i mask = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); __m128i reversed = _mm_shuffle_epi8(o, mask); __m128i v1 = _mm_xor_si128(b, reversed); __m128i mask6 = _mm_set1_epi8(6); __m128i v2 = _mm_and_si128(v1, mask6); __m128i mask4 = _mm_set1_epi8(4); __m128i v3 = _mm_cmpeq_epi8(v2, mask4); unsigned int vmask = _mm_movemask_epi8(v3); // substitutions += sizeof(__m128i) - __builtin_popcount(vmask); substitutions -= __builtin_popcount(vmask); } offset += vec_offset * sizeof(__m128i); for (; offset < length; offset++) { if (!is_complement(self[offset], other[length - 1 - offset])) { substitutions++; } } return substitutions; } phylonium-1.6/libs/seqcmp.c000066400000000000000000000031351416332546600160050ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2019 (C) Fabian Klötzl */ #include "seqcmp.h" #include "config.h" #include #define UNLIKELY(X) __builtin_expect((X), 0) size_t seqcmp_generic(const char *begin, const char *other, size_t length) { assert(begin != NULL); assert(other != NULL); size_t substitutions = 0; size_t i = 0; for (; i < length; i++) { if (UNLIKELY(begin[i] != other[i])) { substitutions++; } } return substitutions; } #ifdef ENABLE_X86_SIMD seqcmp_fn *seqcmp_select(void) { // As ifunc resolvers are called before any constructors run, we explicitly // have to initialize the cpu model detector. // https://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html __builtin_cpu_init(); #ifdef ENABLE_AVX512 if (__builtin_cpu_supports("popcnt") && __builtin_cpu_supports("avx512bw") && __builtin_cpu_supports("avx512vl")) { return seqcmp_avx512; } else #endif if (__builtin_cpu_supports("popcnt") && __builtin_cpu_supports("avx2")) { return seqcmp_avx2; } else if (__builtin_cpu_supports("popcnt") && __builtin_cpu_supports("sse2")) { return seqcmp_sse2; } else { return seqcmp_generic; } } #if __has_attribute(ifunc) && HAVE_FUNC_ATTRIBUTE_IFUNC size_t seqcmp(const char *begin, const char *other, size_t length) __attribute__((ifunc("seqcmp_select"))); #else size_t seqcmp(const char *begin, const char *other, size_t length) { return (seqcmp_select())(begin, other, length); } #endif #else size_t seqcmp(const char *begin, const char *other, size_t length) { return seqcmp_generic(begin, other, length); } #endif phylonium-1.6/libs/seqcmp.h000066400000000000000000000012031416332546600160040ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2019 © Fabian Klötzl */ #pragma once #ifdef __cplusplus extern "C" { #endif #include #include "config.h" size_t seqcmp(const char *begin, const char *other, size_t length); #ifdef ENABLE_X86_SIMD size_t seqcmp_sse2(const char *begin, const char *other, size_t length); size_t seqcmp_avx2(const char *begin, const char *other, size_t length); #ifdef ENABLE_AVX512 size_t seqcmp_avx512(const char *begin, const char *other, size_t length); #endif #endif typedef size_t(seqcmp_fn)(const char *begin, const char *other, size_t length); #ifdef __cplusplus } #endif phylonium-1.6/libs/seqcmp_avx2.c000066400000000000000000000026341416332546600167500ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 (C) Fabian Klötzl */ #include #include #include #include "seqcmp.h" typedef __m256i vec_type; size_t seqcmp_avx2(const char *begin, const char *other, size_t length) { assert(begin != NULL); assert(other != NULL); size_t substitutions = 0; size_t offset = 0; const size_t vec_bytes = sizeof(vec_type); size_t vec_offset = 0; size_t vec_length = (length / vec_bytes) & ~(size_t)1; // round down size_t equal = 0; for (; vec_offset < vec_length; vec_offset++) { vec_type begin_chunk; vec_type other_chunk; memcpy(&begin_chunk, begin + vec_offset * vec_bytes, vec_bytes); memcpy(&other_chunk, other + vec_offset * vec_bytes, vec_bytes); vec_type comp = _mm256_cmpeq_epi8(begin_chunk, other_chunk); unsigned int vmask = _mm256_movemask_epi8(comp); equal += __builtin_popcount(vmask); vec_offset++; // second pass memcpy(&begin_chunk, begin + vec_offset * vec_bytes, vec_bytes); memcpy(&other_chunk, other + vec_offset * vec_bytes, vec_bytes); comp = _mm256_cmpeq_epi8(begin_chunk, other_chunk); vmask = _mm256_movemask_epi8(comp); equal += __builtin_popcount(vmask); } substitutions = vec_offset * vec_bytes - equal; offset += vec_offset * vec_bytes; for (; offset < length; offset++) { if (begin[offset] != other[offset]) { substitutions++; } } return substitutions; } phylonium-1.6/libs/seqcmp_avx512.c000066400000000000000000000020351416332546600171110ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 (C) Fabian Klötzl */ #include "seqcmp.h" #include #include #include typedef __m256i vec_type; size_t seqcmp_avx512(const char *begin, const char *other, size_t length) { assert(begin != NULL); assert(other != NULL); size_t substitutions = 0; const size_t vec_bytes = sizeof(vec_type); size_t vec_offset = 0; size_t vec_length = length / vec_bytes; size_t equal = 0; for (; vec_offset < vec_length; vec_offset++) { vec_type begin_chunk; vec_type other_chunk; memcpy(&begin_chunk, begin + vec_offset * vec_bytes, vec_bytes); memcpy(&other_chunk, other + vec_offset * vec_bytes, vec_bytes); unsigned int vmask = _mm256_cmpeq_epi8_mask(begin_chunk, other_chunk); equal += __builtin_popcount(vmask); } substitutions = vec_length * vec_bytes - equal; size_t offset = vec_offset * vec_bytes; for (; offset < length; offset++) { if (begin[offset] != other[offset]) { substitutions++; } } return substitutions; } phylonium-1.6/libs/seqcmp_sse2.c000066400000000000000000000026031416332546600167400ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 (C) Fabian Klötzl */ #include "seqcmp.h" #include #include #include typedef __m128i vec_type; size_t seqcmp_sse2(const char *begin, const char *other, size_t length) { assert(begin != NULL); assert(other != NULL); size_t substitutions = 0; const size_t vec_bytes = sizeof(vec_type); size_t vec_offset = 0; size_t vec_length = (length / vec_bytes) & ~(size_t)1; // round down size_t equal = 0; for (; vec_offset < vec_length; vec_offset++) { vec_type begin_chunk; vec_type other_chunk; memcpy(&begin_chunk, begin + vec_offset * vec_bytes, vec_bytes); memcpy(&other_chunk, other + vec_offset * vec_bytes, vec_bytes); vec_type comp = _mm_cmpeq_epi8(begin_chunk, other_chunk); unsigned int vmask = _mm_movemask_epi8(comp); equal += __builtin_popcount(vmask); vec_offset++; // second pass memcpy(&begin_chunk, begin + vec_offset * vec_bytes, vec_bytes); memcpy(&other_chunk, other + vec_offset * vec_bytes, vec_bytes); comp = _mm_cmpeq_epi8(begin_chunk, other_chunk); vmask = _mm_movemask_epi8(comp); equal += __builtin_popcount(vmask); } substitutions = vec_offset * vec_bytes - equal; size_t offset = vec_offset * vec_bytes; for (; offset < length; offset++) { if (begin[offset] != other[offset]) { substitutions++; } } return substitutions; } phylonium-1.6/m4/000077500000000000000000000000001416332546600137365ustar00rootroot00000000000000phylonium-1.6/m4/ax_cxx_compile_stdcxx_11.m4000066400000000000000000000456471416332546600211200ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # Copyright (c) 2019 Enji Cooper # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 11 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L ]]) phylonium-1.6/m4/ax_gcc_func_attribute.m4000066400000000000000000000204101416332546600205170ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html # =========================================================================== # # SYNOPSIS # # AX_GCC_FUNC_ATTRIBUTE(ATTRIBUTE) # # DESCRIPTION # # This macro checks if the compiler supports one of GCC's function # attributes; many other compilers also provide function attributes with # the same syntax. Compiler warnings are used to detect supported # attributes as unsupported ones are ignored by default so quieting # warnings when using this macro will yield false positives. # # The ATTRIBUTE parameter holds the name of the attribute to be checked. # # If ATTRIBUTE is supported define HAVE_FUNC_ATTRIBUTE_. # # The macro caches its result in the ax_cv_have_func_attribute_ # variable. # # The macro currently supports the following function attributes: # # alias # aligned # alloc_size # always_inline # artificial # cold # const # constructor # constructor_priority for constructor attribute with priority # deprecated # destructor # dllexport # dllimport # error # externally_visible # fallthrough # flatten # format # format_arg # gnu_format # gnu_inline # hot # ifunc # leaf # malloc # noclone # noinline # nonnull # noreturn # nothrow # optimize # pure # sentinel # sentinel_position # unused # used # visibility # warning # warn_unused_result # weak # weakref # # Unsupported function attributes will be tested with a prototype # returning an int and not accepting any arguments and the result of the # check might be wrong or meaningless so use with care. # # LICENSE # # Copyright (c) 2013 Gabriele Svelto # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 10 AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) AC_CACHE_CHECK([for __attribute__(($1))], [ac_var], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([ m4_case([$1], [alias], [ int foo( void ) { return 0; } int bar( void ) __attribute__(($1("foo"))); ], [aligned], [ int foo( void ) __attribute__(($1(32))); ], [alloc_size], [ void *foo(int a) __attribute__(($1(1))); ], [always_inline], [ inline __attribute__(($1)) int foo( void ) { return 0; } ], [artificial], [ inline __attribute__(($1)) int foo( void ) { return 0; } ], [cold], [ int foo( void ) __attribute__(($1)); ], [const], [ int foo( void ) __attribute__(($1)); ], [constructor_priority], [ int foo( void ) __attribute__((__constructor__(65535/2))); ], [constructor], [ int foo( void ) __attribute__(($1)); ], [deprecated], [ int foo( void ) __attribute__(($1(""))); ], [destructor], [ int foo( void ) __attribute__(($1)); ], [dllexport], [ __attribute__(($1)) int foo( void ) { return 0; } ], [dllimport], [ int foo( void ) __attribute__(($1)); ], [error], [ int foo( void ) __attribute__(($1(""))); ], [externally_visible], [ int foo( void ) __attribute__(($1)); ], [fallthrough], [ int foo( void ) {switch (0) { case 1: __attribute__(($1)); case 2: break ; }}; ], [flatten], [ int foo( void ) __attribute__(($1)); ], [format], [ int foo(const char *p, ...) __attribute__(($1(printf, 1, 2))); ], [gnu_format], [ int foo(const char *p, ...) __attribute__((format(gnu_printf, 1, 2))); ], [format_arg], [ char *foo(const char *p) __attribute__(($1(1))); ], [gnu_inline], [ inline __attribute__(($1)) int foo( void ) { return 0; } ], [hot], [ int foo( void ) __attribute__(($1)); ], [ifunc], [ int my_foo( void ) { return 0; } static int (*resolve_foo(void))(void) { return my_foo; } int foo( void ) __attribute__(($1("resolve_foo"))); ], [leaf], [ __attribute__(($1)) int foo( void ) { return 0; } ], [malloc], [ void *foo( void ) __attribute__(($1)); ], [noclone], [ int foo( void ) __attribute__(($1)); ], [noinline], [ __attribute__(($1)) int foo( void ) { return 0; } ], [nonnull], [ int foo(char *p) __attribute__(($1(1))); ], [noreturn], [ void foo( void ) __attribute__(($1)); ], [nothrow], [ int foo( void ) __attribute__(($1)); ], [optimize], [ __attribute__(($1(3))) int foo( void ) { return 0; } ], [pure], [ int foo( void ) __attribute__(($1)); ], [sentinel], [ int foo(void *p, ...) __attribute__(($1)); ], [sentinel_position], [ int foo(void *p, ...) __attribute__(($1(1))); ], [returns_nonnull], [ void *foo( void ) __attribute__(($1)); ], [unused], [ int foo( void ) __attribute__(($1)); ], [used], [ int foo( void ) __attribute__(($1)); ], [visibility], [ int foo_def( void ) __attribute__(($1("default"))); int foo_hid( void ) __attribute__(($1("hidden"))); int foo_int( void ) __attribute__(($1("internal"))); int foo_pro( void ) __attribute__(($1("protected"))); ], [warning], [ int foo( void ) __attribute__(($1(""))); ], [warn_unused_result], [ int foo( void ) __attribute__(($1)); ], [weak], [ int foo( void ) __attribute__(($1)); ], [weakref], [ static int foo( void ) { return 0; } static int bar( void ) __attribute__(($1("foo"))); ], [ m4_warn([syntax], [Unsupported attribute $1, the test may fail]) int foo( void ) __attribute__(($1)); ] )], []) ], dnl GCC doesn't exit with an error if an unknown attribute is dnl provided but only outputs a warning, so accept the attribute dnl only if no warning were issued. [AS_IF([test -s conftest.err], [AS_VAR_SET([ac_var], [no])], [AS_VAR_SET([ac_var], [yes])])], [AS_VAR_SET([ac_var], [no])]) ]) AS_IF([test yes = AS_VAR_GET([ac_var])], [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_FUNC_ATTRIBUTE_$1), 1, [Define to 1 if the system has the `$1' function attribute])], []) AS_VAR_POPDEF([ac_var]) ]) phylonium-1.6/man/000077500000000000000000000000001416332546600141715ustar00rootroot00000000000000phylonium-1.6/man/phylonium.1.in000066400000000000000000000055431416332546600167130ustar00rootroot00000000000000.TH PHYLONIUM "1" "2020-11-03" "@VERSION@" "phylonium manual" .SH NAME phylonium \- rapidly estimate evolutionary distances .SH SYNOPSIS .B phylonium [\fIOPTIONS...\fR] \fIFILES\fR... .SH DESCRIPTION \fBphylonium\fR estimates the evolutionary distance between closely related genomes. For this \fBphylonium\fR reads the input sequences from \fIFASTA\fR files and computes the pairwise evolutionary distance. All sequences in one file are considered to be contigs of the same genome. .SH OUTPUT The output is a symmetrical distance matrix in \fIPHYLIP\fR format, with each entry representing divergence with a positive, real number. A distance of zero means that two sequences are identical, whereas other values are estimates for the nucleotide substitution rate (Jukes-Cantor corrected). For technical reasons the comparison might fail and no estimate can be computed. In such cases \fInan\fR is printed. This either means that the input sequences were too short (<200bp) or too diverse (K>0.5) for our method to work properly. .SH OPTIONS .TP \fB\-2\fR, \fB\-\-2pass\fR After a first run a 'central' sequence is chosen as reference for the second pass. This improves the accuracy when the first reference is an outgroup. .TP \fB\-b\fR \fIINT\fR, \fB\-\-bootstrap\fR=\fIINT\fR Compute multiple distance matrices, with \fIn-1\fR bootstrapped from the first. See the paper Klötzl & Haubold (2016) for a detailed explanation. .TP \fB\-r\fR \fIFILE\fR Define the reference sequence. .TP \fB\-t\fR \fIINT\fR, \fB\-\-threads\fR=\fIINT\fR The number of threads to be used; by default, all available processors are used. .br Multithreading is only available if \fBphylonium\fR was compiled with OpenMP support. .TP \fB\-v\fR, \fB\-\-verbose\fR Prints additional information, including the amount of found homology. Apply multiple times for extra verboseness. .TP \fB\-h\fR, \fB\-\-help\fR Prints the synopsis and an explanation of available options. .TP \fB\-\-version\fR Outputs version information and acknowledgments. .SH COPYRIGHT Copyright \(co 2017 - 2020 Fabian Klötzl License GPLv3+: GNU GPL version 3 or later. .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. The full license text is available at . .PP .SH ACKNOWLEDGMENTS 1) Algorithms: Ohlebusch, E. (2013). Bioinformatics Algorithms. Sequence Analysis, Genome Rearrangements, and Phylogenetic Reconstruction. pp 118f. .br 2) SA construction: Mori, Y. (2005). Short description of improved two\-stage suffix sorting algorithm. .br 3) Bootstrapping: Klötzl, F. and Haubold, B. (2016). Support Values for Genome Phylogenies 4) Phylonium: Klötzl, F. and Haubold, B. (2020). Phylonium: Fast Estimation of Evolutionary Distances from Large Samples of Similar Genomes .SH BUGS .SS Reporting Bugs Please report bugs to . phylonium-1.6/src/000077500000000000000000000000001416332546600142055ustar00rootroot00000000000000phylonium-1.6/src/Makefile.am000066400000000000000000000015671416332546600162520ustar00rootroot00000000000000bin_PROGRAMS = phylonium phylonium_SOURCES = phylonium.cxx io.cxx io.h sequence.cxx sequence.h esa.cxx esa.h process.cxx process.h evo_model.cxx evo_model.h global.h phylonium_CPPFLAGS = $(OPENMP_CFLAGS) -std=c++14 -Wall -Wextra -I$(top_srcdir)/libs phylonium_CXXFLAGS = $(OPENMP_CXXFLAGS) -O2 -ggdb phylonium_LDADD = \ $(top_builddir)/libs/libcompat.a \ $(top_builddir)/libs/libpfasta.a \ $(top_builddir)/libs/librevseqcmp.a \ $(top_builddir)/libs/libseqcmp.a if ENABLE_X86_SIMD phylonium_LDADD+=\ $(top_builddir)/libs/librevseqcmp_avx2.a \ $(top_builddir)/libs/librevseqcmp_ssse3.a \ $(top_builddir)/libs/libseqcmp_avx2.a \ $(top_builddir)/libs/libseqcmp_sse2.a if ENABLE_AVX512 phylonium_LDADD+=$(top_builddir)/libs/libseqcmp_avx512.a endif endif .PHONY: format debug debug: CXXFLAGS=-O0 -fno-omit-frame-pointer -ggdb debug: phylonium format: clang-format -i *.h *.cxx phylonium-1.6/src/esa.cxx000066400000000000000000000327741416332546600155160ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ /** * @file * @brief ESA functions * * This file contains various functions that operate on an enhanced suffix * array. The basic algorithms originate from the book of Ohlebusch * "Bioinformatics Algorithms" (2013). Most of these were heavily modified * for improved performance. One example is the lcp-cache. * * The ESA structure defined in esa.h contains a `cache` field. This cache is * used to quickly look up lcp-intervals. Consider the queries "AAGT" and * "AACG". In both cases the interval for "AA" has to be looked up in the * ESA. If we simply store the interval for "AA" in the cache, once and use it * for each query we are significantly faster (up to 7 times). */ #include #include #include #include "esa.h" #include "global.h" /** @brief The prefix length up to which LCP-intervals are cached. */ const size_t CACHE_LENGTH = 4; /** @brief Map a code to the character. */ constexpr char code2char(ssize_t code) noexcept { switch (code & 0x3) { case 0: return 'A'; case 1: return 'C'; case 2: return 'G'; case 3: return 'T'; } return '\0'; } static std::array char2code_table = ([]() { std::array char2code_table = {0}; std::fill(char2code_table.begin(), char2code_table.end(), (ssize_t)-1); char2code_table['A'] = 0; char2code_table['C'] = 1; char2code_table['G'] = 2; char2code_table['T'] = 3; return char2code_table; })(); /** @brief Map a character to a two bit code. */ /*constexpr*/ ssize_t char2code(char c) noexcept { return char2code_table[c]; } /** @brief Initializes an ESA. * * This function initializes an ESA with respect to the provided sequence. * @param seq - The sequence */ esa::esa(const sequence &seq) : m_size{seq.size() * 2 + 1} { m_master = seq; S = m_master.get_nucl() + '#' + reverse(m_master.get_nucl()); SA = std::make_unique(m_size); divsufsort(reinterpret_cast(S.c_str()), SA.get(), m_size); init_LCP(); init_CLD(); init_FVC(); init_cache(); } /** @brief Fills the LCP-Interval cache. * * Traversing the virtual suffix tree, created by SA, LCP and CLD is rather * slow. Hence we create a cache, holding the LCP-interval for a prefix of a * certain length ::CACHE_LENGTH. This function it the entry point for the * cache filling routine. */ void esa::init_cache() { this->cache = std::make_unique(1 << (2 * CACHE_LENGTH)); char str[CACHE_LENGTH + 1]; str[CACHE_LENGTH] = '\0'; saidx_t m = left_child(m_size); lcp_interval ij = {.l = LCP[m], .i = 0, .j = m_size - 1, .m = m}; init_cache_dfs(str, 0, ij); } /** @brief Fills the cache — one char at a time. * * This function is a depth first search on the virtual suffix tree and fills * the cache. Or rather it calls it self until some value to cache is * calculated. This function is a recursive version of get_interval but with * more edge cases. * * @param str - The current prefix. * @param pos - The length of the prefix. * @param in - The LCP-interval of prefix[0..pos-1]. */ void esa::init_cache_dfs(char *str, size_t pos, lcp_interval in) { // we are not yet done, but the current strings do not exist in the subject. if (pos < CACHE_LENGTH && in.i == -1 && in.j == -1) { init_cache_fill(str, pos, in); return; } // we are past the caching length if (pos >= CACHE_LENGTH) { init_cache_fill(str, pos, in); return; } lcp_interval ij; // iterate over all nucleotides for (int code = 0; code < 4; ++code) { str[pos] = code2char(code); ij = get_interval(in, str[pos]); // fail early if (ij.i == -1 && ij.j == -1) { // if the current extension cannot be found, will with previous one init_cache_fill(str, pos + 1, in); continue; } // singleton if (ij.i == ij.j) { // fix length ij.l = pos + 1; init_cache_fill(str, pos + 1, ij); continue; } if (ij.l <= (ssize_t)(pos + 1)) { // Continue one level deeper // This is the usual case init_cache_dfs(str, pos + 1, ij); continue; } // The LCP-interval is deeper than expected // Check if it still fits into the cache if ((size_t)ij.l >= CACHE_LENGTH) { // If the lcp-interval exceeds the cache depth, stop here and fill init_cache_fill(str, pos + 1, in); continue; } /* At this point the prefix `str` of length `pos` has been found. * However, the call to `getInterval` above found an interval with * an LCP value bigger than `pos`. This means that not all elongations * (more precise: just one) of `str` appear in the subject. Thus fill * all values with the matched result to far and continue only with * the one special substring. */ init_cache_fill(str, pos + 1, in); char non_acgt = 0; // fast forward size_t k = pos + 1; for (; k < (size_t)ij.l; k++) { // In some very edgy edge cases the lcp-interval `ij` // contains a `;` or another non-acgt character. Since we // cannot cache those, break. char c = S[SA[ij.i] + k]; if (char2code(c) < 0) { non_acgt = 1; break; } str[k] = c; } // We are skipping intervals here. Maybe for each of them we should also // fill the cache. However, I haven't yet figured out how to do that // properly and whether it is worth it. if (non_acgt) { init_cache_fill(str, k, ij); } else { init_cache_dfs(str, k, ij); } } } /** @brief Fills the cache with a given value. * * Given a prefix and a value this function fills the cache beyond this point * the value. * * @param str - The current prefix. * @param pos - The length of the prefix. * @param in - The LCP-interval of prefix[0..pos-1]. */ void esa::init_cache_fill(char *str, size_t pos, lcp_interval in) { if (pos < CACHE_LENGTH) { for (int code = 0; code < 4; ++code) { str[pos] = code2char(code); init_cache_fill(str, pos + 1, in); } } else { ssize_t code = 0; for (size_t i = 0; i < CACHE_LENGTH; ++i) { code <<= 2; code |= char2code(str[i]); } cache[code] = in; } } /** * @brief Initializes the FVC (first variant character) array. * * The FVC is of my own invention and simply defined as * `FVC[i] = S[SA[i]+LCP[i]]`. This expression is constantly used in * get_interval. By precomputing the result, we have less memory * accesses, less cache misses, and thus improved runtimes of up to 15% * faster matching. This comes at a negligible cost of increased memory. */ void esa::init_FVC() { FVC = std::make_unique(m_size); FVC[0] = '\0'; auto FVC_p = FVC.get(); auto LCP_p = LCP.get(); auto SA_p = SA.get(); for (size_t i = m_size; i; i--, FVC_p++, SA_p++, LCP_p++) { *FVC_p = S[*SA_p + *LCP_p]; } } /** @brief Initializes the CLD (child) array. * * See Ohlebusch. */ void esa::init_CLD() { CLD = std::make_unique(m_size + 1); typedef struct pair_s { saidx_t idx, lcp; } pair_t; auto stack = std::make_unique(m_size + 1); pair_t *top = stack.get(); // points at the topmost filled element pair_t last; right_child(0) = m_size; top->idx = 0; top->lcp = -1; // iterate over all elements for (ssize_t k = 1; k < m_size + 1; k++) { while (LCP[k] < top->lcp) { // top->lcp is a leaf last = *top--; // link all elements of same lcp value in a chain while (top->lcp == last.lcp) { right_child(top->idx) = last.idx; last = *top--; } // store the l-index of last if (LCP[k] < top->lcp) { right_child(top->idx) = last.idx; } else { left_child(k) = last.idx; } } // continue one level deeper top++; top->idx = k; top->lcp = LCP[k]; } } /** * This function computed the LCP array, given the suffix array. Thereto it uses * a special `phi` array, which makes it slightly faster than the original * linear-time algorithm by Kasai et al. */ void esa::init_LCP() { saidx_t len = m_size; // Allocate new memory // The LCP array is one element longer than S. LCP = std::make_unique(len + 1); LCP[0] = -1; LCP[len] = -1; // Allocate temporary arrays auto PHI = std::make_unique(len); auto &PLCP = PHI; //? PHI[SA[0]] = -1; saidx_t k; ssize_t i; for (i = 1; i < len; i++) { PHI[SA[i]] = SA[i - 1]; } ssize_t l = 0; for (i = 0; i < len; i++) { k = PHI[i]; if (k != -1) { while (S[k + l] == S[i + l]) { l++; } PLCP[i] = l; l--; if (l < 0) l = 0; } else { PLCP[i] = -1; } } // unpermutate the LCP array for (i = 1; i < len; i++) { LCP[i] = PLCP[SA[i]]; } } /** @brief For the lcp-interval of string `w` compute the interval for `wa` * * Say, we already know the LCP-interval ij for a string `w`. Now we want to * check if `wa` may also be found in the ESA and thus in the subject. So we * look for the sub interval of `ij` in which all strings feature an `a` as * the next character. If such a sub interval is found, its boundaries are * returned. * * @param ij - The lcp-interval for `w`. * @param a - The next character. * @returns The lcp-interval one level deeper. */ lcp_interval esa::get_interval(lcp_interval ij, char a) const { saidx_t i = ij.i; saidx_t j = ij.j; // check for singleton or empty interval if (i == j) { if (S[SA[i] + ij.l] != a) { ij.i = ij.j = -1; } return ij; } int m = ij.m; int l = ij.l; char c = S[SA[i] + l]; goto SoSueMe; do { c = FVC[i]; SoSueMe: if (c == a) { /* found ! */ if (i != m - 1) { // found interval contains >1 element saidx_t n = left_child(m); ij = (lcp_interval){.l = LCP[n], .i = i, .j = m - 1, .m = n}; } else { // empty or singleton // doing left_child(m) is not valid in this case! ij = (lcp_interval){.l = LCP[i], .i = i, .j = i, .m = -1}; } return ij; } if (c > a) { break; } i = m; if (i == j) { break; // singleton interval, or `a` not found } m = right_child(m); } while (/*m != "bottom" && */ LCP[m] == l); // final sanity check if (i != ij.i ? FVC[i] == a : S[SA[i] + l] == a) { ij.i = i; ij.j = j; /* Also return the length of the LCP interval including `a` and * possibly even more characters. Note: l + 1 <= LCP[m] */ ij.l = LCP[m]; ij.m = m; } else { ij.i = ij.j = -1; } return ij; } /** @brief Compute the longest match of a query with the subject. * * The *longest match* is the core concept of `andi`. Its simply defined as the * longest prefix of a query Q appearing anywhere in the subject S. Talking * about genetic sequences, a match is a homologous region, likely followed by a * SNP. * * This function returns the interval for where the longest match of the query * can be found in the ESA. Thereto it expects a starting interval for the * search. * * @param query - The query sequence. * @param qlen - The length of the query. Should correspond to `strlen(query)`. * @param k - The starting index into the query. * @param ij - The LCP interval for the string `query[0..k]`. * @returns The LCP interval for the longest prefix. */ lcp_interval esa::get_match_from(const char *query, size_t qlen, saidx_t k, lcp_interval ij) const { if (ij.i == -1 && ij.j == -1) { return ij; } // fail early on singleton intervals. if (ij.i == ij.j) { // try to extend the match. See line 513 below. saidx_t p = SA[ij.i]; size_t k = ij.l; // const char *S = this->S; for (; k < qlen && S[p + k]; k++) { if (S[p + k] != query[k]) { ij.l = k; return ij; } } ij.l = k; return ij; } saidx_t l, i, j; lcp_interval res = ij; // Loop over the query until a mismatch is found do { // Get the subinterval for the next character. ij = get_interval(ij, query[k]); i = ij.i; j = ij.j; // If our match cannot be extended further, return. if (i == -1 && j == -1) { res.l = k; return res; } res.i = ij.i; res.j = ij.j; l = qlen; if (i < j && ij.l < l) { /* Instead of making another look up we can use the LCP interval * calculated in get_interval */ l = ij.l; } // By definition, the kth letter of the query was matched. k++; // Extend the match for (int p = SA[i]; k < l; k++) { if (S[p + k] != query[k]) { res.l = k; return res; } } } while (k < (ssize_t)qlen); res.l = qlen; return res; } /** @brief Get a match. * * Given an ESA and a string Q find the longest prefix of Q that matches * somewhere in C. This search is done entirely via jumping around in the ESA, * and thus is slow. * * @param query - The query string — duh. * @param qlen - The length of the query. * @returns the lcp interval of the match. */ lcp_interval esa::get_match(const char *query, size_t qlen) const { saidx_t m = left_child(m_size); lcp_interval ij = {.l = LCP[m], .i = 0, .j = m_size - 1, .m = m}; return get_match_from(query, qlen, 0, ij); } /** @brief Compute the LCP interval of a query. For a certain prefix length of * the query its LCP interval is retrieved from a cache. Hence this is faster * than the naive `get_match`. If the cache fails to provide a proper value, we * fall back to the standard search. * * @param query - The query sequence. * @param qlen - The length of the query. Should correspond to `strlen(query)`. * @returns The LCP interval for the longest prefix. */ lcp_interval esa::get_match_cached(const char *query, size_t qlen) const { if (qlen <= CACHE_LENGTH) return get_match(query, qlen); ssize_t offset = 0; for (size_t i = 0; i < CACHE_LENGTH && offset >= 0; i++) { offset <<= 2; offset |= char2code(query[i]); } if (offset < 0) { return get_match(query, qlen); } lcp_interval ij = cache[offset]; if (ij.i == -1 && ij.j == -1) { return get_match(query, qlen); } return get_match_from(query, qlen, ij.l, ij); } phylonium-1.6/src/esa.h000066400000000000000000000054731416332546600151370ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ /** * @file * @brief This header contains the declarations for functions in esa.cxx. * */ #pragma once #include #include #include #include "sequence.h" /** * @brief Represents LCP-Intervals. * * This struct is used to represent LCP-intervals. The member `i` should * coincide with the lower bound whereas `j` is the upper bound. Both bounds * are inclusive. So if `i == j` the interval contains exactly one element, * namely `i`. To represent an empty interval please use `i == j == -1`. * Other variants, such as `i == j == -2` can be used to indicate an error. * The common prefix length is denoted by l and should always be non-negative. * Variables of this type are often called `ij`. */ typedef struct { /** @brief The common prefix length */ saidx_t l; /** @brief lower bound */ saidx_t i; /** @brief upper bound */ saidx_t j; /** The new middle. */ saidx_t m; } lcp_interval; /** @brief A full text index. * Basically, this is a bunch of arrays working together for fast lookups. */ class esa { /** Length of the arrays. */ int m_size = 0; /** The sequence at the basis of the ESA. */ sequence m_master{}; /** The Longest Common Prefix array. */ std::unique_ptr LCP; /** The child array. */ std::unique_ptr CLD; /** The First Variant Character array. See Klötzl (2015) for an explanation. */ std::unique_ptr FVC; /** A cache to speed up the look-up. */ std::unique_ptr cache; public: /** The Suffix Array */ std::unique_ptr SA; /** The base string */ std::string S{""}; /** @brief a reasonable default constructor */ esa() = default; esa(const sequence &); lcp_interval get_match(const char *, size_t) const; lcp_interval get_match_cached(const char *, size_t) const; /** @brief Get length of the arrays. * @returns the length of the arrays. */ auto size() const noexcept { return m_size; } private: void init_LCP(); void init_CLD(); void init_FVC(); void init_cache(); void init_cache_dfs(char *, size_t, lcp_interval); void init_cache_fill(char *, size_t, lcp_interval); lcp_interval get_match_from(const char *, size_t, saidx_t, lcp_interval) const; lcp_interval get_interval(lcp_interval ij, char a) const; /** @brief Get the right child of a node. * @param idx - Index of the node. * @returns the index of the right child node. */ auto &right_child(ssize_t idx) const noexcept { return CLD[idx]; } /** @brief Get the left child of a node. * @param idx - Index of the node. * @returns the index of the left child node. */ auto &left_child(ssize_t idx) const noexcept { return CLD[idx - 1]; } }; #ifdef DEBUG char code2char(ssize_t code); #endif // DEBUG phylonium-1.6/src/evo_model.cxx000066400000000000000000000072251416332546600167100ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2020 © Fabian Klötzl */ #include "evo_model.h" #include #include #include #include #include #include #include #include #include "revseqcmp.h" #include "seqcmp.h" extern std::mt19937 prng; /** @brief Turn a nucleotide into a 2bit representation. * This function uses a clever way to transform nucleotides (ACGT) into a 2bit * representation. Maybe even too clever. * @param nucl - The nucleotide. * @returns returns a 2bit representation. */ int evo_model::hash(char nucl) noexcept { if (nucl < 'A') return -1; nucl &= 6; nucl ^= nucl >> 1; return nucl >> 1; } /** @brief Compare two characters and update the counts. * @param a - A nucleotide from one sequence. * @param b - The nucleotide from the other sequence. */ void evo_model::account(char a, char b) noexcept { homologs++; if (a != b) { substitutions++; } } /** Tell the compiler a branch is unlikely to be taken. */ #define UNLIKELY(X) __builtin_expect(X, 0) /** @brief Compare two sequences. * @param sa - A pointer to a nucleotide sequence. * @param sb - A pointer to another nucleotide sequence. * @param length - The length of the homologous section. */ void evo_model::account(const char *sa, const char *sb, size_t length) noexcept { size_t mutations = seqcmp(sa, sb, length); homologs += length; substitutions += mutations; } /** @brief Compare one sequence with the reverse complement of another. * @param sa - The forward sequence. * @param sb - The sequence of which the reverse complement is of interest. * @param b_offset - The offset of the reverse complement (TODO: get rid of * this). * @param length - The length of the homologous region. */ void evo_model::account_rev(const char *sa, const char *sb, size_t b_offset, size_t length) noexcept { size_t mutations = revseqcmp(sa, sb + b_offset - length, length); homologs += length; substitutions += mutations; } /** @brief Integrate the other count into this one. * @param other - The other substitution counts. * @returns a reference to the updated counts. */ evo_model &evo_model::operator+=(const evo_model &other) noexcept { homologs += other.homologs; substitutions += other.substitutions; return *this; } /** @brief Return the number of homologous nucleotides. * @return the number of homologous nucleotides. */ size_t evo_model::total() const noexcept { return homologs; } /** @brief Estimate the substitution rate. * @returns the rate of substitutions. */ double evo_model::estimate_raw(bool zero_on_error) const noexcept { size_t nucl = total(); if (nucl == 0) return zero_on_error ? 0.0 : NAN; size_t SNPs = substitutions; return SNPs / (double)nucl; } /** @brief Estimate the evolutionary distance via the Jukes-Cantor correction. * @returns the evolutionary distance. */ double evo_model::estimate_JC(bool zero_on_error) const noexcept { auto dist = estimate_raw(zero_on_error); dist = -0.75 * log(1.0 - (4.0 / 3.0) * dist); // jukes cantor // fix negative zero return dist <= 0.0 ? 0.0 : dist; } /** @brief Bootstrap a new distance using a method by Haubold & Klötzl (2016). * @returns a new instance of homologous nucleotides. */ evo_model evo_model::bootstrap() const { auto ret = *this; // copy auto subst_rate = substitutions / (double)homologs; std::binomial_distribution<> d(homologs, subst_rate); ret.substitutions = d(prng); return ret; } /** @brief Computes the 'coverage'. * @returns the number of nucleotides covered by this homology model. */ double evo_model::coverage(size_t length) const noexcept { return (double)total() / length; } phylonium-1.6/src/evo_model.h000066400000000000000000000026701416332546600163340ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2021 © Fabian Klötzl */ #pragma once #include #include #include #include /** @brief An "evolutionary model". Basically this class counts mutations. */ class evo_model { protected: /** Count of substitutions among homologous nucleotides. */ size_t substitutions = 0; /** Number of homologous nucleotides. */ size_t homologs = 0; public: static int hash(char c) noexcept; /** @brief Reasonable default constructor. */ evo_model() = default; void account(char a, char b) noexcept; void account(const char *stra, const char *strb, size_t length) noexcept; void account_rev(const char *stra, const char *strb, size_t b_offset, size_t length) noexcept; evo_model &operator+=(const evo_model &other) noexcept; size_t total() const noexcept; double estimate_raw(bool zero_on_error = false) const noexcept; double estimate_JC(bool zero_on_error = false) const noexcept; double coverage(size_t length) const noexcept; /** @brief Compare two counts of homologous nucleotides by length. * @param self - This count. * @param other - The count to compare to. * @returns the model with the larger count of homologous nucleotides. */ static const evo_model &select_by_total(const evo_model &self, const evo_model &other) { return self.total() < other.total() ? other : self; } evo_model bootstrap() const; }; phylonium-1.6/src/global.h000066400000000000000000000024021416332546600156140ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ #include enum flags { none, verbose, extra_verbose, complete_deletion = 4, print_progress = 8, print_positions = 16 }; extern int FLAGS; extern int THREADS; extern int RETURN_CODE; extern double ANCHOR_P_VALUE; extern long unsigned int BOOTSTRAP; extern std::string REFPOS_FILE_NAME; /** * @brief This macro is used to print a warning and make the program exit with * an failure exit code, later. */ #define soft_err(...) \ do { \ RETURN_CODE |= EXIT_FAILURE; \ warn(__VA_ARGS__); \ } while (0) /** * @brief This macro is used to print a warning and make the program exit with * an failure exit code, later. */ #define soft_errx(...) \ do { \ RETURN_CODE |= EXIT_FAILURE; \ warnx(__VA_ARGS__); \ } while (0) phylonium-1.6/src/io.cxx000066400000000000000000000146611416332546600153500ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ /** * @file * @brief This file contains the definitions for various io methods. */ #include "io.h" #include #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "sequence.h" extern size_t reference_index; /** @brief extracts the genome name from a file path * * We try to be clever about the genome name. Given the file * path we extract just the file name. ie. path/file.ext -> file * This obviously fails on Windows. * * @param s_file_name - The file path * @returns just the genome name */ std::string extract_genome(const std::string &s_file_name) { // find the last path separator auto left = s_file_name.rfind('/'); left = (left == std::string::npos) ? 0 : left + 1; // left is the position one of to the right of the path separator // find the extension auto right = s_file_name.rfind('.'); if (right != std::string::npos) { auto ext = s_file_name.substr(right); if (ext == ".fa" || ext == ".fas" || ext == ".fasta") { // pass and strip } else { // don't strip unkown extention right = s_file_name.size(); } } else { right = s_file_name.size(); } // copy only the file name, not its path or extension return s_file_name.substr(left, right - left); } /** * @brief This function reads sequences from a file. * @param file_name - The file to read. * @param prefix - A prefix for the name. */ std::vector read_fasta(std::string s_file_name, std::string prefix) { std::vector sequences{}; const char *file_name = s_file_name.c_str(); int file_descriptor = open(file_name, O_RDONLY); if (file_descriptor < 0) { err(errno, "%s", file_name); } auto parser = pfasta_init(file_descriptor); if (parser.errstr) { errx(1, "%s: %s", file_name, parser.errstr); } while (!parser.done) { auto record = pfasta_read(&parser); if (parser.errstr) { errx(1, "%s: %s", file_name, parser.errstr); } sequences.emplace_back(prefix + record.name, filter_nucl(record.sequence)); pfasta_record_free(&record); } pfasta_free(&parser); close(file_descriptor); return sequences; } genome read_genome(std::string file_name) { std::string species{extract_genome(file_name.c_str())}; return genome{species, read_fasta(file_name, "")}; } void print_warnings(const std::vector &queries, const std::vector &names, const std::vector &dist_matrix, const std::vector &matrix) { auto N = names.size(); for (size_t i = 0; i < N; i++) { for (size_t j = 0; j < i; j++) { auto index = i * N + j; auto dist = dist_matrix[index]; if (std::isnan(dist)) { const char str[] = { "For the two sequences '%s' and '%s' the distance " "computation failed and is reported as nan."}; soft_errx(str, names[i].c_str(), names[j].c_str()); } if (!std::isnan(dist)) { double cov1 = matrix[index].coverage(queries[i].size()); double cov2 = matrix[index].coverage(queries[j].size()); const char str[] = { "For the two sequences '%s' and '%s' less than 20%% " "homology were found (%f and %f, respectively)."}; if (cov1 < 0.2 || cov2 < 0.2) { soft_errx(str, names[i].c_str(), names[j].c_str(), cov1, cov2); } } } } } void just_print(const std::vector &names, const std::vector &dist_matrix) { auto N = names.size(); // Produce output in PHYLIP distance matrix format std::cout << N << std::endl; std::cout.precision(4); std::cout << std::scientific; for (size_t i = 0; i < N; i++) { std::cout << names[i]; for (size_t j = 0; j < N; j++) { auto index = i * N + j; auto dist = (i == j ? 0.0 : dist_matrix[index]); std::cout << " " << dist; } std::cout << std::endl; } } void print_matrix(const std::vector &queries, const std::vector &matrix) { auto N = queries.size(); auto names = std::vector(N); std::transform(std::begin(queries), std::end(queries), std::begin(names), [&](const sequence &seq) { return seq.get_name(); }); auto dist_matrix = std::vector(N * N, NAN); std::transform(std::begin(matrix), std::end(matrix), std::begin(dist_matrix), [](const evo_model &em) { return em.estimate_JC(); }); // print warnings before distance matrix print_warnings(queries, names, dist_matrix, matrix); just_print(names, dist_matrix); if (BOOTSTRAP) { auto neu = std::vector(N * N); for (auto k = (size_t)0; k < BOOTSTRAP; k++) { std::transform(std::begin(matrix), std::end(matrix), std::begin(neu), [](const evo_model &em) { return em.bootstrap(); }); std::transform( std::begin(neu), std::end(neu), std::begin(dist_matrix), [](const evo_model &em) { return em.estimate_JC(); }); just_print(names, dist_matrix); } } double sum = 0.0; size_t counter = 0; for (size_t i = 0; i < N; i++) { for (size_t j = 0; j < i; j++) { auto index = i * N + j; auto dist = dist_matrix[index]; if (!std::isnan(dist)) { double cov1 = matrix[index].coverage(queries[i].size()); double cov2 = matrix[index].coverage(queries[j].size()); sum += cov1 + cov2; counter += 2; } } } size_t aln_aligned = 0; size_t aln_total = 0; for (size_t i = 0; i < N; i++) { if (i == reference_index) continue; auto index = reference_index * N + i; aln_aligned += matrix[index].total(); aln_total += queries[i].size(); } if (FLAGS & flags::verbose) { std::cerr << "avg coverage:\t" << sum / counter << std::endl; std::cerr << "alignment:\t" << aln_aligned << "\t" << aln_total << "\t" << aln_aligned / (double)aln_total << std::endl; } } void print_matrix(const sequence &subject, const std::vector &queries, const std::vector &matrix) { auto N = queries.size(); print_matrix(queries, matrix); if (FLAGS & flags::verbose) { auto M = [&matrix, N = N](size_t i, size_t j) -> const evo_model & { return matrix[i * N + j]; }; auto covf = std::ofstream(subject.get_name() + ".abscov"); covf << "Absolute Coverages:\n"; for (size_t i = 0; i < N; i++) { covf << queries[i].get_name(); for (size_t j = 0; j < N; j++) { covf << " " << std::setw(8) << std::setprecision(4) << M(i, j).total(); } covf << std::endl; } } } phylonium-1.6/src/io.h000066400000000000000000000011011416332546600147560ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ /** * @file * @brief This header contains function declarations for io procedures. */ #ifndef _IO_H_ #define _IO_H_ #include #include #include #include "sequence.h" genome read_genome(std::string); void print_matrix(const sequence &subject, const std::vector &queries, const std::vector &matrix); void print_matrix(const std::vector &queries, const std::vector &matrix); #endif // _IO_H_ phylonium-1.6/src/phylonium.cxx000066400000000000000000000264101416332546600167600ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2020 © Fabian Klötzl */ /** * @file * * This is the main file. It contains functions to parse the commandline * arguments, read files etc. * * @brief The main file * @author Fabian Klötzl * * @section License * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details at * http://www.gnu.org/copyleft/gpl.html * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "global.h" #include "io.h" #include "pfasta.h" #include "process.h" #include "sequence.h" #ifdef _OPENMP #include #endif double ANCHOR_P_VALUE = 0.025; int FLAGS = flags::none; int THREADS = 1; long unsigned int BOOTSTRAP = 0; int RETURN_CODE = EXIT_SUCCESS; std::string REFPOS_FILE_NAME = ""; std::mt19937 prng; // TODO: This is a hack and should be redone at some point. size_t reference_index = 0; void usage(int); void version(void); using mat_type = std::vector; size_t pick_second_pass(std::vector &sequences, const mat_type &matrix); size_t pick_first_pass(std::vector &sequences); void cleanup_names(const std::string &reference_name, std::vector &file_names); template auto properlySeededRandomEngine() { std::random_device source; std::random_device::result_type random_data[(N - 1) / sizeof(source()) + 1]; std::generate(std::begin(random_data), std::end(random_data), std::ref(source)); std::seed_seq seeds(std::begin(random_data), std::end(random_data)); return T(seeds); } int main(int argc, char *argv[]) { prng = properlySeededRandomEngine(); int version_flag = 0; bool two_pass = false; auto reference_name = std::string{}; enum { P_AUTO, P_NEVER, P_ALWAYS } progress = P_AUTO; static struct option long_options[] = { {"2pass", no_argument, NULL, '2'}, {"bootstrap", required_argument, NULL, 'b'}, {"complete-deletion", no_argument, NULL, 0}, {"help", no_argument, NULL, 'h'}, {"progress", optional_argument, NULL, 0}, {"threads", required_argument, NULL, 't'}, {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, &version_flag, 1}, {0, 0, 0, 0}}; #ifdef _OPENMP // Use all available processors by default. THREADS = omp_get_num_procs(); #endif // parse arguments while (1) { int option_index; int c = getopt_long(argc, argv, "2b:hp:r:t:v", long_options, &option_index); if (c == -1) { break; } switch (c) { case 0: { auto arg_str = std::string(long_options[option_index].name); if (arg_str == "complete-deletion") { FLAGS |= flags::complete_deletion; break; } if (arg_str == "progress") { if (!optarg || strcasecmp(optarg, "always") == 0) { progress = P_ALWAYS; } else if (strcasecmp(optarg, "auto") == 0) { progress = P_AUTO; } else if (strcasecmp(optarg, "never") == 0) { progress = P_NEVER; } else { warnx("invalid argument to --progress '%s'. Expected " "one of 'auto', 'always', or 'never'.", optarg); } break; } break; } case '2': { two_pass = true; break; } case 'b': { errno = 0; char *end; long unsigned int bootstrap = strtoul(optarg, &end, 10); if (errno || end == optarg || *end != '\0' || bootstrap == 0) { soft_errx( "Expected a positive number for -b argument, but '%s' " "was given. Ignoring -b argument.", optarg); break; } BOOTSTRAP = bootstrap - 1; break; } case 'h': usage(EXIT_SUCCESS); break; case 'p': { FLAGS |= flags::print_positions; FLAGS |= flags::complete_deletion; REFPOS_FILE_NAME = std::string(optarg); break; } case 'r': { reference_name = optarg; break; } case 't': { #ifdef _OPENMP errno = 0; char *end; long unsigned int threads = strtoul(optarg, &end, 10); if (errno || end == optarg || *end != '\0') { warnx("Expected a number for -t argument, but '%s' was " "given. Ignoring -t argument.", optarg); break; } if (threads > (long unsigned int)omp_get_num_procs()) { warnx( "The number of threads to be used, is greater then the " "number of available processors; Ignoring -t %lu " "argument.", threads); break; } THREADS = threads; #else warnx( "This version of phylonium was built without OpenMP " "and thus " "does not support multi threading. Ignoring -t argument."); #endif break; } case 'v': FLAGS |= FLAGS & flags::verbose ? flags::extra_verbose : flags::verbose; break; case '?': /* intentional fall-through */ default: usage(EXIT_FAILURE); break; } } if (FLAGS & flags::print_positions) { // avoid overwriting files auto file = std::ifstream(REFPOS_FILE_NAME); if (file.good()) { errx(1, "output file '%s' already exists", REFPOS_FILE_NAME.c_str()); } } if (version_flag) { version(); } // determine whether to print a progress bar if (progress == P_AUTO) { progress = isatty(STDERR_FILENO) ? P_ALWAYS : P_NEVER; } if (progress == P_ALWAYS) { FLAGS |= flags::print_progress; } argc -= optind; argv += optind; auto file_names = std::vector(argv, argv + argc); // add missing reference names to file names if (reference_name != "") { cleanup_names(reference_name, file_names); } if (file_names.size() < 2) { usage(EXIT_FAILURE); } // at max `file_names` many files have to be read. auto queries = std::vector(file_names.size()); // read all genomes #pragma omp parallel for num_threads(THREADS) if (pfasta_threadsafe()) for (size_t i = 0; i < file_names.size(); i++) { queries[i] = join(read_genome(file_names[i])); } // avoid copying sequences if (reference_name == "") { pick_first_pass(queries); } else { auto it = std::find(file_names.begin(), file_names.end(), reference_name); auto index = it - file_names.begin(); reference_index = index; } auto matrix = process(queries[reference_index], queries); if (two_pass) { auto new_reference_index = pick_second_pass(queries, matrix); auto new_matrix = process(queries[new_reference_index], queries); print_matrix(queries, new_matrix); } else { print_matrix(queries, matrix); } return RETURN_CODE; } /** @brief Picks the references for the second pass according to some criterion. * * For the second pass we make an informed choice about the reference(s) using * the distances computed in the first pass. There are a number of possible * options: * * - ingroup * - outgroup * - best coverage * * At the moment the most central sequence is chosen. * * @param sequences - The input sequences. * @param matrix - The distance matrix from the first pass. * @returns a new reference (index). */ size_t pick_second_pass(std::vector &sequences, const std::vector &matrix) { auto ret = std::vector>(); auto size = sequences.size(); auto dist_matrix = std::vector(size * size, NAN); std::transform(std::begin(matrix), std::end(matrix), std::begin(dist_matrix), [](const evo_model &em) { return em.estimate_JC(true); }); auto central_value = std::numeric_limits::max(); auto central_index = (size_t)0; for (size_t i = 0; i < size; i++) { auto sum = std::accumulate(dist_matrix.begin() + i * size, dist_matrix.begin() + i * size + size, 0.0); if (sum < central_value) { central_value = sum; central_index = i; } } ret.push_back(sequences[central_index]); reference_index = central_index; return central_index; } /** @brief Picks the references for the first pass according to some criterion. * * For the first pass we make an best-effort choice for a suitable reference if * none was supplied by the user. There are a number of possible options: * * - size (smallest, medium, largest) * - gc content (medium) * - assembly quality * * At the moment a sequence of medium length is chosen. * * @param sequences - The input sequences. * @returns a new reference (index). */ size_t pick_first_pass(std::vector &sequences) { // pick a reference by some criterion. auto ret = std::vector>(sequences.begin(), sequences.end()); std::nth_element(ret.begin(), ret.begin() + ret.size() / 2, ret.end(), [](const sequence &a, const sequence &b) { return a.size() < b.size(); }); auto &reference = ret[ret.size() / 2].get(); // recover which element the reference was in the original array auto it = std::find(sequences.begin(), sequences.end(), reference); reference_index = it - sequences.begin(); if (FLAGS & flags::verbose) { std::cerr << "chosen reference: " << reference.get_name() << std::endl; } return reference_index; } void cleanup_names(const std::string &reference_name, std::vector &file_names) { file_names.push_back(reference_name); std::sort(file_names.begin(), file_names.end()); auto split = std::unique(file_names.begin(), file_names.end()); file_names.erase(split, file_names.end()); } /** @brief Prints the usage and then exits. * @param status - The return status. * @returns - It doesn't. */ void usage(int status) { const char str[] = { "Usage: phylonium [OPTIONS] FILES...\n" "\tFILES... can be any sequence of FASTA files, each file representing " "one genome.\n\n" "Options:\n" " -2, --2pass Enable two-pass algorithm\n" " -b, --bootstrap=N Print additional bootstrap matrices\n" " --complete-deletion Delete the whole aligned column in case of " "gaps\n" " -p FILE Print reference positions to FILE (implies " "complete deletion)\n" " -r FILE Set the reference genome\n" #ifdef _OPENMP " -t, --threads=N The number of threads to be used; by default, " "all available processors are used\n" #endif " -v, --verbose Print additional information\n" " -h, --help Display this help and exit\n" " --version Output version information and " "acknowledgments\n"}; fprintf(status == EXIT_SUCCESS ? stdout : stderr, "%s", str); exit(status); } /** * @brief This function just prints the version string and then aborts * the program. */ void version(void) { const char str[] = { "phylonium " VERSION "\n" "Copyright (C) 2017 - 2019 Fabian Klötzl\n" "License GPLv3+: GNU GPL version 3 or later " "\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n\n"}; printf("%s", str); exit(EXIT_SUCCESS); } phylonium-1.6/src/process.cxx000066400000000000000000000514351416332546600164170ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ #include "process.h" #include #include #include #include #include #include #include #include #include #include "esa.h" #include "evo_model.h" #include "global.h" #include "sequence.h" double shuprop(size_t, double, size_t); /** @brief Find an element based on its index. * @param first - Iterator tho the beginning of a range. * @param last - Iterator to the end of a range. * @param p - A predicate function return true or false depending on the index. * @returns an iterator to the first element for which the predicate returns * true, or last if none. */ template InputIt find_if_i(InputIt first, InputIt last, UnaryPredicate p) { auto begin = first; for (; first != last; ++first) { if (p(first - begin)) { return first; } } return last; } /** @brief Remove elements from a range using their index. * @param first - Iterator to the beginning of a range. * @param last - Iterator to the end of a range. * @param p - A predicate function returning true for the indices to be removed. * @returns The border at the end of the remaining elements. */ template ForwardIt remove_if_i(ForwardIt first, ForwardIt last, UnaryPredicate p) { auto begin = first; first = find_if_i(first, last, p); if (first != last) for (ForwardIt i = first; ++i != last;) if (!p(i - begin)) *first++ = std::move(*i); return first; } /** * @brief Calculates the minimum anchor length. * * Given some parameters calculate the minimum length for anchors according * to the distribution from Haubold et al. (2009). * * @param p - The probability with which an anchor is allowed to be random. * @param g - The the relative amount of GC in the subject. * @param l - The length of the subject. * @returns The minimum length of an anchor. */ size_t min_anchor_length(double p, double g, size_t l) { size_t x = 1; while (shuprop(x, g / 2, l) < 1 - p) { x++; } return x; } /** * @brief Calculates the binomial coefficient of n and k. * * We used to use gsl_sf_lnchoose(xx,kk) for this functionality. * After all, why implement something that has already been done? * Well, the reason is simplicity: GSL is used for only this one * function and the input (n<=20) is not even considered big. * Hence its much easier to have our own implementation and ditch * the GSL dependency even if that means our code is a tiny bit * less optimized and slower. * * @param n - The n part of the binomial coefficient. * @param k - analog. * @returns (n choose k) */ size_t binomial_coefficient(size_t n, size_t k) { if (n <= 0 || k > n) { return 0; } if (k == 0 || k == n) { return 1; } if (k > n - k) { k = n - k; } size_t res = 1; for (size_t i = 1; i <= k; i++) { res *= n - k + i; res /= i; } return res; } /** * @brief Given `x` this function calculates the probability of a shustring * with a length less than `x`. * * Let X be the longest shortest unique substring (shustring) at any position. * Then this function computes P{X <= x} with respect to the given parameter * set. See Haubold et al. (2009). * * @param x - The maximum length of a shustring. * @param p - The the half of the relative amount of GC in the DNA. * @param l - The length of the subject. * @returns The probability of a certain shustring length. */ double shuprop(size_t x, double p, size_t l) { double xx = (double)x; double ll = (double)l; size_t k; double s = 0.0; for (k = 0; k <= x; k++) { double kk = (double)k; double t = pow(p, kk) * pow(0.5 - p, xx - kk); s += pow(2, xx) * (t * pow(1 - t, ll)) * (double)binomial_coefficient(x, k); if (s >= 1.0) { s = 1.0; break; } } return s; } /** @brief Compute the length of the common prefix. * Given two strings return the number of characters being equal at the * beginning of both string. * @param S - One string. * @param Q - Another string. * @param remaining - The maximum number of characters to investigate. * @returns the LCP. */ size_t lcp(const char *S, const char *Q, size_t remaining) { size_t length = 0; while (length < remaining && S[length] == Q[length]) { length++; } return length; } /** * @brief Compute homologies between two sequences. * * A homology is defined as a region on two sequences which two flanking * anchors. These anchors are long exact matches. For a detailed explanation see * Haubold et al 2014. * * @param ref - the reference sequence and ESA. * @param threshold - minimum length of an anchor. * @param seq - the query. * @returns a list of homologies. */ auto anchor_homologies(const esa &ref, size_t threshold, const sequence &seq) { auto hv = std::vector(); size_t border = ref.size() / 2; size_t query_length = seq.size(); size_t last_pos_Q = 0; size_t last_pos_S = 0; size_t last_length = 0; // This variable indicates that the last anchor was the right anchor of a // pair. bool last_was_right_anchor = false; size_t this_pos_Q = 0; size_t this_pos_S; size_t this_length; auto current = homology(0, 0); auto anchor = [&]() { auto inter = ref.get_match_cached(seq.c_str() + this_pos_Q, query_length - this_pos_Q); this_length = std::max(inter.l, 0); this_pos_S = ref.SA[inter.i]; return inter.i == inter.j && this_length >= threshold; }; auto lucky_anchor = [&]() { size_t advance = this_pos_Q - last_pos_Q; size_t gap = this_pos_Q - last_pos_Q - last_length; size_t try_pos_S = last_pos_S + advance; if (try_pos_S >= static_cast(ref.size()) || gap > threshold) { return false; } this_pos_S = try_pos_S; this_length = lcp(&seq.c_str()[this_pos_Q], &ref.S.c_str()[try_pos_S], query_length - this_pos_Q); return this_length >= threshold; }; // Iterate over the complete query. while (this_pos_Q < query_length) { if (lucky_anchor() || anchor()) { // anchor size_t end_S = last_pos_S + last_length; size_t end_Q = last_pos_Q + last_length; if (this_pos_S > end_S && this_pos_Q - end_Q == this_pos_S - end_S && (this_pos_S < border) == (last_pos_S < border)) { // right anchor current.extend(this_pos_Q - end_Q + this_length); last_was_right_anchor = true; } else { // no anchor pair, left anchor! if (last_was_right_anchor || last_length / 2 >= threshold) { // push last current.reverseEh(border); hv.push_back(std::move(current)); } current = homology(this_pos_S, this_pos_Q, this_length); // this is not right anchor last_was_right_anchor = false; } // Cache values for later last_pos_Q = this_pos_Q; last_pos_S = this_pos_S; last_length = this_length; } // Advance this_pos_Q += this_length + 1; } // Very special case: The sequences are identical if (last_length >= query_length) { current = homology(last_pos_S, 0, query_length); } if (last_was_right_anchor || last_length / 2 >= threshold) { current.reverseEh(border); hv.push_back(std::move(current)); } return hv; } evo_model compare(const sequence &sa, const homology &ha, const sequence &sb, const homology &hb); evo_model compare(const sequence &sa, const std::vector &ha, const sequence &sb, const std::vector &hb); /** @brief Remove all homologies that overlap any other. * * The result of `anchor_homologies` is a set of homologies. However, some of * these may overlap on the reference interfering with mutation counting later * on. Thus matches have to be filtered. * * This function removes all homologies which overlap with any other one. * * @param pile - in out parameter with homologies. */ void filter_overlaps_strict(std::vector &pile) { if (pile.size() < 2) return; auto next = std::begin(pile) + 1; size_t border = 0; // capture mode has to be by-reference, otherwise bad things happen. // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81482 auto filter = [&](const homology &homo) { bool overlaps_left = border > homo.index_reference_projected; border = std::max(border, homo.index_reference_projected + homo.length); bool overlaps_right = homo.overlaps(*next); next++; return overlaps_left || overlaps_right; }; // the last homology can only overlap to the left auto remove_last = (pile.end() - 2)->overlaps(*(pile.end() - 1)); auto split = std::remove_if(pile.begin(), pile.end() - 1, filter); if (!remove_last) { std::swap(*split, *(pile.end() - 1)); split++; } pile.erase(split, pile.end()); } /** @brief Maximizes the number of homologous nucleotides by carefully removing * all but one of the overlapping homologies. * * The result of `anchor_homologies` is a set of homologies. However, some of * these may overlap on the reference interfering with mutation counting later * on. Thus matches have to be filtered. * * This function selectively reduces overlapping stacks to only a single * homology. It does this by chaining neighboring homologies. The chain with * most nucleotides is then kept, all homologies not in it get removed. * * @param pile - in out parameter with homologies. */ void filter_overlaps_max(std::vector &pile) { if (pile.size() < 2) return; size_t size = pile.size(); auto predecessor_buffer = std::vector(size + 1, -1); auto score_buffer = std::vector(size + 1, 0); ssize_t *predecessor = predecessor_buffer.data() + 1; ssize_t *score = score_buffer.data() + 1; predecessor[0] = -1; score[0] = pile[0].length; for (ssize_t i = 1; i < (ssize_t)size; i++) { // find maximum, so far auto max_value = (ssize_t)0; auto max_index = (ssize_t)-1; for (ssize_t k = 0; k < i; k++) { if (!pile[k].ends_left_of(pile[i])) continue; if (score[k] > max_value) { max_value = score[k]; max_index = k; } } predecessor[i] = max_index; score[i] = score[max_index] + pile[i].length; } auto visited = std::vector(size, false); auto that = std::max_element(score_buffer.begin(), score_buffer.end()); ssize_t index = that - score_buffer.begin() - 1; while (index >= 0) { visited[index] = true; index = predecessor[index]; } auto split = remove_if_i(pile.begin(), pile.end(), [&](size_t index) { return !visited[index]; }); pile.erase(split, pile.end()); } /** @brief Process the input and do a lot of stuff. * @param subject - The subject sequence. * @param queries - All sequences. * @returns a "matrix" of mutation counts. */ std::vector process(const sequence &subject, const std::vector &queries) { auto N = queries.size(); auto ref = esa(subject); // seq + # + reverse auto homologies = std::vector>(N); auto gc = gc_content(subject.get_nucl()); size_t threshold = min_anchor_length(ANCHOR_P_VALUE, gc, ref.size()); if (FLAGS & flags::verbose) { std::cerr << "ref: " << subject.get_name() << std::endl; } int print_progress = FLAGS & flags::print_progress; if (print_progress) { fprintf(stderr, "Mapping %zu sequences: %5.1f%% (%zu/%zu)", N, 0.0, (size_t)0, N); } size_t progress_counter = 0; // now compare every sequence to the subject #pragma omp parallel for num_threads(THREADS) for (size_t j = 0; j < N; j++) { // std::cerr << "query: " << j << std::endl; auto query = queries[j]; auto hvlocal = anchor_homologies(ref, threshold, query); std::sort(begin(hvlocal), end(hvlocal), [](const homology &self, const homology &other) { return self.starts_left_of(other); }); filter_overlaps_max(hvlocal); #pragma omp critical homologies[j] = std::move(hvlocal); #pragma omp atomic progress_counter++; if (print_progress) { double progress = 100.0 * (double)progress_counter / N; #pragma omp critical fprintf(stderr, "\rMapping %zu sequences: %5.1f%% (%zu/%zu)", N, progress, progress_counter, N); } } if (print_progress) { fprintf(stderr, ", done.\n"); } ////////////////////////////// // reduce to core genome if (FLAGS & flags::complete_deletion) { homologies = complete_delete(homologies); } if (FLAGS & flags::print_positions) { std::vector get_segsites(const sequence &sa, const homology &ha, const sequence &sb, const homology &hb); // after complete deletion all sequences are restricted to the same // positions on the reference. Print those positions. const auto &homos = homologies[0]; size_t counter = 1; auto refpos_file = std::ofstream(REFPOS_FILE_NAME); for (size_t i = 0; i < homos.size(); i++) { const auto &h = homos[i]; auto is_segsite = std::vector(h.length, 0); for (size_t m = 0; m < queries.size(); m++) { auto foo = get_segsites(queries[0], h, queries[m], homologies[m][i]); for (size_t t = 0; t < foo.size(); t++) { is_segsite[t] |= foo[t]; } } auto segsite_pos = std::vector{}; for (size_t t = 0; t < is_segsite.size(); t++) { if (is_segsite[t]) { segsite_pos.push_back(t); } } auto start = h.start(); auto end = h.end(); refpos_file << ">part" << counter++ << "\t(" << (start + 1) << ".." << (end + 1) << ") " << segsite_pos.size(); for (auto pos : segsite_pos) { refpos_file << " " << (pos + 1); } refpos_file << std::endl; refpos_file << std::string(subject.begin() + start, subject.begin() + end) << std::endl; } } ////////////////////////////// auto matrix = std::vector(N * N); auto M = [&matrix, N = N](size_t i, size_t j) -> evo_model & { return matrix[i * N + j]; }; progress_counter = 0; #pragma omp parallel for num_threads(THREADS) for (size_t i = 0; i < N; i++) { for (size_t j = i + 1; j < N; j++) { M(j, i) = M(i, j) = compare(queries[i], homologies[i], queries[j], homologies[j]); #pragma omp atomic progress_counter++; } if (print_progress) { size_t local_progress_counter; size_t num_comparisons = (N * N - N) / 2; #pragma omp atomic read local_progress_counter = progress_counter; double progress = 100.0 * (double)local_progress_counter / num_comparisons; #pragma omp critical fprintf(stderr, "\rComparing the sequences: %5.1f%% (%zu/%zu)", progress, local_progress_counter, num_comparisons); } } if (print_progress) { fprintf(stderr, ", done.\n"); } return matrix; } /** @brief Compare two sequences based on their precomputed homologies wrt. the * reference. * @param sa - One sequence. * @param ha - The homologies of sequence A with the reference. * @param sb - Another sequence. * @param hb - The homologies of sequence B with the reference. * @returns mutation counts. */ evo_model compare(const sequence &sa, const std::vector &ha, const sequence &sb, const std::vector &hb) { auto mutations = evo_model(); const auto &other = hb; auto right_ptr = begin(other); auto pile = std::vector(); for (const auto &homo : ha) { auto ends_left_of_homo = [&homo](const auto &other_homo) { return other_homo.ends_left_of(homo); }; auto overlaps_homo = [&homo](const auto &other_homo) { return other_homo.overlaps(homo); }; // erase homologies which are done auto split = std::remove_if(pile.begin(), pile.end(), ends_left_of_homo); pile.erase(split, end(pile)); // skip elements left of homo! right_ptr = std::find_if_not(right_ptr, other.end(), ends_left_of_homo); // add new homologies auto far_right_ptr = find_if_not(right_ptr, other.end(), overlaps_homo); /* Note that this cannot be merged with the find above * into a single copy_if. The list of homologies is sorted! * Thus this combination is linear, whereas copy_if would * have a O(n^2) complexity. */ std::copy(right_ptr, far_right_ptr, std::back_inserter(pile)); right_ptr = far_right_ptr; // compare homo against pile for (const auto &other_homo : pile) { mutations += compare(sa, homo, sb, other_homo); } } return mutations; } /** @brief Compare the homologous region of two sequences. * @param sa - One sequence. * @param ha - The homology of sequence A with the reference. * @param sb - Another sequence. * @param hb - The homology of sequence B with the reference. * @returns mutation counts. */ evo_model compare(const sequence &sa, const homology &ha, const sequence &sb, const homology &hb) { if (!ha.overlaps(hb)) { return evo_model{}; } auto count = evo_model{}; size_t common_start = std::max(ha.start(), hb.start()); size_t common_end = std::min(ha.end(), hb.end()); assert(common_start < common_end); size_t length = common_end - common_start; auto hat = ha.trim(common_start, common_end); auto hbt = hb.trim(common_start, common_end); if (ha.direction == hb.direction && ha.direction == homology::dir::forward) { // simple comparison count.account(sa.c_str() + hat.start_query(), sb.c_str() + hbt.start_query(), length); } else if (ha.direction == hb.direction && ha.direction == homology::dir::reverse) { // no need for double complement count.account(sa.c_str() + hat.start_query(), sb.c_str() + hbt.start_query(), length); } else if (hb.direction == homology::dir::reverse) { // reverse b count.account_rev(sa.c_str() + hat.start_query(), sb.c_str(), hbt.end_query(), length); } else if (ha.direction == homology::dir::reverse) { // reverse a count.account_rev(sb.c_str() + hbt.start_query(), sa.c_str(), hat.end_query(), length); } return count; } char *is_segsite(const char *begin, const char *other, char *out, size_t length); char *is_segsite_rev(const char *begin, const char *other, char *out, size_t length); std::vector get_segsites(const sequence &sa, const homology &ha, const sequence &sb, const homology &hb) { if (!ha.overlaps(hb)) { return {}; } size_t common_start = std::max(ha.start(), hb.start()); size_t common_end = std::min(ha.end(), hb.end()); assert(common_start < common_end); size_t length = common_end - common_start; auto hat = ha.trim(common_start, common_end); auto hbt = hb.trim(common_start, common_end); auto ret = std::vector(length, 0); if (ha.direction == hb.direction && ha.direction == homology::dir::forward) { // simple comparison is_segsite(sa.c_str() + hat.start_query(), sb.c_str() + hbt.start_query(), ret.data(), length); } else if (ha.direction == hb.direction && ha.direction == homology::dir::reverse) { is_segsite(sa.c_str() + hat.start_query(), sb.c_str() + hbt.start_query(), ret.data(), length); std::reverse(ret.begin(), ret.end()); } else if (hb.direction == homology::dir::reverse) { // reverse b is_segsite_rev(sa.c_str() + hat.start_query(), sb.c_str() + hbt.end_query() - length, ret.data(), length); } else if (ha.direction == homology::dir::reverse) { is_segsite_rev(sb.c_str() + hbt.start_query(), sa.c_str() + hat.end_query() - length, ret.data(), length); } return ret; } char *is_segsite(const char *begin, const char *other, char *out, size_t length) { for (size_t i = 0; i < length; i++) { out[i] = begin[i] != other[i]; } return out + length; } char *is_segsite_rev(const char *begin, const char *other, char *out, size_t length) { for (size_t i = 0; i < length; i++) { int xorr = begin[i] ^ other[length - i - 1]; out[i] = (xorr & 6) != 4; } return out + length; } std::vector> complete_delete(const std::vector> &homologies) { auto size = homologies.size(); auto core_homologies = std::vector>(size); auto ins = std::vector>>(); for (auto &vec : core_homologies) { ins.push_back(std::back_inserter(vec)); } using hom_iter = std::vector::const_iterator; auto front = std::vector(); auto back = std::vector(); for (auto &homs : homologies) { front.push_back(std::begin(homs)); back.push_back(std::end(homs)); } auto front_has_not_reached_back = [&]() { return std::inner_product(front.begin(), front.end(), back.begin(), true, std::logical_and<>(), std::less<>()); }; while (front_has_not_reached_back()) { auto starts = std::vector(size); std::transform(front.begin(), front.end(), starts.begin(), [](hom_iter hit) { return hit->start(); }); auto common_start_elem = std::max_element(starts.begin(), starts.end()); auto ends = std::vector(size); std::transform(front.begin(), front.end(), ends.begin(), [](hom_iter hit) { return hit->end(); }); auto common_end_elem = std::min_element(ends.begin(), ends.end()); if (*common_start_elem < *common_end_elem) { // generate overlap std::transform( front.begin(), front.end(), ins.begin(), [=](auto hit) { return hit->trim(*common_start_elem, *common_end_elem); }); } auto leftmost = std::distance(std::begin(ends), common_end_elem); front[leftmost]++; } return core_homologies; } phylonium-1.6/src/process.h000066400000000000000000000074341416332546600160440ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ #pragma once #include #include #include "evo_model.h" #include "sequence.h" std::vector process(const sequence &, const std::vector &); class homology { public: /** Store the direction of a match. */ enum class dir { forward, reverse } direction = dir::forward; /** The beginning on the reference genome. */ size_t index_reference = 0; /** The beginning on the forward strand of the reference genome. */ size_t index_reference_projected = 0; /** The beginning on the query genome. */ size_t index_query = 0; /** The length of the homologous region. */ size_t length = 0; /** @brief Reasonable default constructor. */ homology() = default; /** @brief Create a new homology from coordinates. */ homology(size_t ir, size_t iq, size_t l = 0) noexcept : direction{dir::forward}, index_reference{ir}, index_reference_projected{ir}, index_query{iq}, length{l} { } auto start() const noexcept { return index_reference_projected; } auto end() const noexcept { return index_reference_projected + length; } auto start_query() const noexcept { return index_query; } auto end_query() const noexcept { return index_query + length; } /** @brief Extend the current homologous region to the right. * @param stride - amount of elongation. * @returns the new length. */ auto extend(size_t stride) noexcept { return length += stride; } /** @brief Transform coordinates, if necessary. * If the homology is within the reverse region of the reference project the * coordinates onto the forward strand. * @param reference_length - The length of the reference. */ void reverseEh(size_t reference_length) noexcept { if (index_reference < reference_length) return; // hack, see esa::esa() index_reference_projected = 2 * reference_length + 1 - length - index_reference; direction = dir::reverse; } /** @brief Check for overlap. * @param other - another homology. * @returns true iff this homology overlaps the argument. */ bool overlaps(const homology &other) const noexcept { if (start() == other.start()) { return true; } if (starts_left_of(other)) return !ends_left_of(other); if (other.starts_left_of(*this)) return !other.ends_left_of(*this); // no more case // should not be executed return false; } /** @brief Determine the order of two homologies. * @param other - a homology to compare with. * @returns true if this homology starts left of the argument wrt. the * reference genome. */ bool starts_left_of(const homology &other) const noexcept { return start() < other.start(); } /** @brief Determine the order of two homologies. * @param other - a homology to compare with. * @returns true if this homology ends left of the argument wrt. the * reference genome. */ bool ends_left_of(const homology &other) const noexcept { return end() <= other.start(); } homology trim(size_t start, size_t end) const { if (end <= start) return *this; // invalid input range, ignore. // Carefully handle cases where the given range is bigger than *this. auto that = *this; auto offset = start > this->start() && start < this->end() ? start - this->start() : 0; auto drift = this->end() > end && end > this->start() ? this->end() - end : 0; that.index_reference_projected += offset; if (direction == dir::forward) { that.index_reference += offset; that.index_query += offset; } else { that.index_reference += drift; that.index_query += drift; } assert(this->length > offset + drift); that.length = this->length - offset - drift; return that; } }; std::vector> complete_delete(const std::vector> &homologies); phylonium-1.6/src/sequence.cxx000066400000000000000000000072111416332546600165420ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ /** * @file * @brief Sequence utilities * * This file contains utility functions for working with DNA sequences. */ #include "sequence.h" #include #include #include #include #include #include #include /** @brief Create a new sequence object. * @param name_ - The new name. * @param nucl_ - The new nucleotide sequence. */ sequence::sequence(std::string name_, std::string nucl_) noexcept : name{std::move(name_)}, nucl{std::move(nucl_)}, length{nucl.size()} { const size_t LENGTH_LIMIT = (INT_MAX - 1) / 2; if (this->size() > LENGTH_LIMIT) { errx(1, "The input sequence %s is too long. The technical limit is %zu.", this->name.c_str(), LENGTH_LIMIT); } } /** @brief Create a FASTA entry from the entry. * @returns a FASTA entry. */ std::string sequence::to_fasta() const { static const ssize_t LINE_LENGTH = 70; auto ret = ">" + get_name() + "\n"; ret.reserve(ret.size() + length + length / LINE_LENGTH); auto it = this->begin(); while (it < this->end()) { auto ll = std::min(this->end() - it, LINE_LENGTH); ret.append(it, it + ll); ret += '\n'; it += ll; } return ret; } /** * @brief Compute the reverse complement. * @param base - The master string. * @returns the reverse complement. */ std::string reverse(const std::string &base) { std::string ret{}; ret.reserve(base.size()); size_t len = base.size(); auto str = new char[len + 1]; for (size_t k = 0; k < len; k++) { char c = base[len - k - 1], d; if (c < 'A') { d = c; } else { d = c ^= (c & 2) ? 4 : 21; // ACGT } str[k] = d; } str[len] = '\0'; ret.replace(0, len, str); delete[] str; return ret; } /** @brief Filter out weird nucleotides. * @param base - The string to filter. * @returns a new string non-canonical nucleotides removed. */ std::string filter_nucl(const std::string &base) { std::string ret{}; ret.reserve(base.size()); size_t new_length = 0; auto str = new char[base.size() + 1]; static std::array table = ([]() { std::array table = {0}; table['A'] = 'A'; table['C'] = 'C'; table['G'] = 'G'; table['T'] = 'T'; table['a'] = 'A'; table['c'] = 'C'; table['g'] = 'G'; table['t'] = 'T'; return table; })(); for (auto c : base) { char d = table[(unsigned char)c]; if (d) { str[new_length] = d; new_length++; } } ret.append(str, new_length); delete[] str; return ret; } /** @brief Compute the GC content. * @param seq - The nucleotide sequence. * @returns the fraction of GC in the nucleotide sequence. */ double gc_content(const std::string &seq) noexcept { size_t gc = 0; size_t length = seq.size(); for (size_t i = 0; i < length; i++) { char masked = seq[i] & 'G' & 'C'; if (masked == ('G' & 'C')) { gc++; } } return static_cast(gc) / length; } /** @brief Linearize the genome into one sequence. * @param gen - The genome. * @returns a sequence consisting of all the contigs joined together. */ sequence join(const genome &gen) { const auto &contigs = gen.get_contigs(); if (contigs.size() == 0) { return sequence(); } if (contigs.size() == 1) { // use genome name, not sequence name. return sequence(gen.get_name(), contigs[0].get_nucl()); } size_t total = gen.get_length(); auto neu = std::string(); neu.reserve(total); // Copy all old sequences and add a `!` in between auto it = contigs.begin(); neu += it->get_nucl(); std::for_each(++it, end(contigs), [&neu](const sequence &seq) { neu += '!'; neu += seq.get_nucl(); }); return sequence(gen.get_name(), neu); } phylonium-1.6/src/sequence.h000066400000000000000000000066111416332546600161720ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ /** * @file * @brief Functions and structures for DNA sequences * */ #pragma once #include #include #include #include /** @brief A class to hold DNA sequences. */ class sequence { /** An identifying name for the sequence. Commonly taken from FASTA header. */ std::string name{}; /** The DNA sequence. */ std::string nucl{}; /** Length of the DNA sequence. */ size_t length{0}; public: /** Reasonable default constructor. */ sequence() = default; sequence(std::string, std::string) noexcept; std::string to_fasta() const; bool operator==(const sequence &other) const { return name == other.name && nucl == other.nucl; } /** @brief Returns the length of the sequence. * @returns the length of the sequence. */ auto size() const noexcept { return length; } /** @brief Return the name. * @returns a copy of the name. */ auto get_name() const { return name; } /** @brief Return the nucleotide sequence. * @returns a constant reference to the nucleotide sequence. */ const auto &get_nucl() const { return nucl; } /** @brief Return an iterator to the beginning of the nucleotide sequence. * @returns an iterator to the beginning of the nucleotide sequence. */ auto begin() const { return nucl.begin(); } /** @brief Return an iterator to the end of the nucleotide sequence. * @returns an iterator to the end of the nucleotide sequence. */ auto end() const { return nucl.end(); } /** @brief Give access to the raw nucleotide string. * @returns a raw pointer to the nucleotides. */ auto c_str() const { return nucl.c_str(); } }; std::string reverse(const std::string &); double gc_content(const std::string &) noexcept; std::string filter_nucl(const std::string &); /** * @brief A class to hold all the sequences from one genome/FASTA file. */ class genome { /** ID for the genome (usually stripped file name). */ std::string name{}; /** Set of sequences. */ std::vector contigs{}; /** Sum of all sequences plus border characters. */ size_t joined_length{0}; public: /** @brief Reasonable default constructor. */ genome() = default; /** @brief Reasonable constructor. * @param _name - New name of the genome. * @param _contigs - New set of sequences. */ genome(std::string _name, std::vector _contigs) noexcept : name{std::move(_name)}, contigs{std::move(_contigs)} { joined_length = std::accumulate( begin(contigs), end(contigs), contigs.size() - 1, [](auto sum, const auto &contig) { return sum + contig.size(); }); } /** @brief Return the name. * @returns reference to the name. */ std::string &get_name() { return name; } /** @brief Return the name. * @returns a constant reference to the name. */ const std::string &get_name() const { return name; } /** @brief Return the set of sequences. * @returns reference to the set of sequences. */ std::vector &get_contigs() { return contigs; } /** @brief Return the set of sequences. * @returns a constant reference to the set of sequences. */ const std::vector &get_contigs() const { return contigs; } /** @brief Return the joined length. * @returns the joined length. */ auto get_length() const noexcept { return joined_length; } }; sequence join(const genome &gen); phylonium-1.6/test/000077500000000000000000000000001416332546600143755ustar00rootroot00000000000000phylonium-1.6/test/Makefile.am000066400000000000000000000021171416332546600164320ustar00rootroot00000000000000check_PROGRAMS= simf unittests simf_SOURCES= simf.cxx simf_CPPFLAGS= -std=c++14 -Wall -Wextra simf_CXXFLAGS= -ggdb unittests_SOURCES= \ $(top_srcdir)/src/esa.cxx \ $(top_srcdir)/src/esa.h \ $(top_srcdir)/src/evo_model.cxx \ $(top_srcdir)/src/evo_model.h \ $(top_srcdir)/src/process.cxx \ $(top_srcdir)/src/process.h \ $(top_srcdir)/src/sequence.cxx \ $(top_srcdir)/src/sequence.h \ catch.hpp \ Tmain.cxx \ Tprocess.cxx \ Tsequence.cxx unittests_CPPFLAGS= -I$(top_srcdir)/src -I$(top_srcdir)/libs -std=c++14 -Wall -Wextra unittests_CXXFLAGS= -ggdb unittests_LDADD = \ $(top_builddir)/libs/libcompat.a \ $(top_builddir)/libs/libpfasta.a \ $(top_builddir)/libs/librevseqcmp.a \ $(top_builddir)/libs/libseqcmp.a if ENABLE_X86_SIMD unittests_LDADD+=\ $(top_builddir)/libs/librevseqcmp_avx2.a \ $(top_builddir)/libs/librevseqcmp_ssse3.a \ $(top_builddir)/libs/libseqcmp_avx2.a \ $(top_builddir)/libs/libseqcmp_sse2.a if ENABLE_AVX512 unittests_LDADD+=$(top_builddir)/libs/libseqcmp_avx512.a endif endif dist_noinst_DATA= simple.sh .PHONY: format format: clang-format -i *.h *.cxx phylonium-1.6/test/Tmain.cxx000066400000000000000000000004201416332546600161650ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include "catch.hpp" #include "global.h" #include double ANCHOR_P_VALUE = 0.025; int FLAGS = flags::none; int THREADS = 1; long unsigned int BOOTSTRAP = 0; int RETURN_CODE = EXIT_SUCCESS; size_t reference_index = 0; std::mt19937 prng; phylonium-1.6/test/Tprocess.cxx000066400000000000000000000066301416332546600167300ustar00rootroot00000000000000#include #include "catch.hpp" #include "process.h" std::string REFPOS_FILE_NAME; void filter_overlaps_max(std::vector &pile); bool operator==(const homology &a, const homology &b) { if (a.start() != b.start()) return false; if (a.end() != b.end()) return false; if (a.start_query() != b.start_query()) return false; if (a.end_query() != b.end_query()) return false; return true; } TEST_CASE("Homology basics") { auto A = homology{0, 0, 10}; auto B = homology{1, 1, 10}; REQUIRE(A.starts_left_of(B)); REQUIRE(!A.ends_left_of(B)); REQUIRE(A.overlaps(B)); auto C = homology{10, 10, 10}; REQUIRE(A.starts_left_of(C)); REQUIRE(A.ends_left_of(C)); REQUIRE(!A.overlaps(C)); // Query coordinate doesn't matter A = homology{0, 23456, 10}; B = homology{1, 678, 10}; C = homology{10, 987, 10}; REQUIRE(A.starts_left_of(B)); REQUIRE(!A.ends_left_of(B)); REQUIRE(A.overlaps(B)); REQUIRE(A.starts_left_of(C)); REQUIRE(A.ends_left_of(C)); REQUIRE(!A.overlaps(C)); auto D = homology{0, 0, 100}.trim(0, 10); A = homology{0, 0, 10}; REQUIRE(D.start() == A.start()); REQUIRE(D.end() == A.end()); REQUIRE(D.start_query() == A.start_query()); REQUIRE(D.end_query() == A.end_query()); } TEST_CASE("Homology filtering") { // two possible beginnings auto pile = std::vector{{0, 0, 10}, {1, 1, 3}}; filter_overlaps_max(pile); REQUIRE(pile.size() == 1); REQUIRE(pile[0] == homology{0, 0, 10}); // overlap in the middle pile = std::vector{ {0, 0, 10}, {10, 10, 10}, {10, 10, 20}, {40, 40, 5}}; auto expected = std::vector{{0, 0, 10}, {10, 10, 20}, {40, 40, 5}}; filter_overlaps_max(pile); REQUIRE(pile == expected); // two possible endings pile = std::vector{ {0, 0, 10}, {10, 10, 10}, {10, 10, 20}, {40, 40, 5}, {42, 42, 2}}; filter_overlaps_max(pile); REQUIRE(pile == expected); // two chains pile = std::vector{{10, 10, 10}, {0, 0, 10}, {20, 20, 10}, {5, 5, 10}, {15, 15, 10}, {25, 25, 10}, {30, 30, 10}}; expected = std::vector{ {0, 0, 10}, {10, 10, 10}, {20, 20, 10}, {30, 30, 10}}; std::sort(begin(pile), end(pile), [](const homology &self, const homology &other) { return self.starts_left_of(other); }); filter_overlaps_max(pile); REQUIRE(pile == expected); } TEST_CASE("Complete deletion") { auto homologies = std::vector>{ {{10, 10, 10}, {110, 110, 20}, {220, 220, 10}, {260, 260, 10}}, {{10, 10, 10}, {120, 120, 20}, {200, 200, 100}}, {{0, 0, 300}, {300, 300, 100}}}; auto expected = std::vector>{ {{10, 10, 10}, {120, 120, 10}, {220, 220, 10}, {260, 260, 10}}, {{10, 10, 10}, {120, 120, 10}, {220, 220, 10}, {260, 260, 10}}, {{10, 10, 10}, {120, 120, 10}, {220, 220, 10}, {260, 260, 10}}}; REQUIRE(complete_delete(homologies) == expected); REQUIRE(complete_delete(expected) == expected); // query coordinates vary per sequence homologies = std::vector>{ {{10, 110, 10}, {110, 210, 20}, {220, 320, 10}, {260, 460, 10}}, {{10, 510, 10}, {120, 620, 20}, {200, 700, 100}}, {{0, 0, 300}, {300, 300, 100}}}; expected = std::vector>{ {{10, 110, 10}, {120, 220, 10}, {220, 320, 10}, {260, 460, 10}}, {{10, 510, 10}, {120, 620, 10}, {220, 720, 10}, {260, 760, 10}}, {{10, 10, 10}, {120, 120, 10}, {220, 220, 10}, {260, 260, 10}}}; REQUIRE(complete_delete(homologies) == expected); } phylonium-1.6/test/Tsequence.cxx000066400000000000000000000020441416332546600170550ustar00rootroot00000000000000#include #include "catch.hpp" #include "sequence.h" TEST_CASE("Sequence basics") { auto s = sequence("Name", "ACGTACGT"); REQUIRE(s.get_name() == "Name"); REQUIRE(s.get_nucl() == "ACGTACGT"); REQUIRE(s.size() == 8); } TEST_CASE("Revcomp") { REQUIRE(reverse("") == ""); REQUIRE(reverse("A") == "T"); REQUIRE(reverse("C") == "G"); REQUIRE(reverse("G") == "C"); REQUIRE(reverse("T") == "A"); REQUIRE(reverse("ACGTACGT") == "ACGTACGT"); auto str = std::string("TACGATCGATCGAAAGCTAGTTCGCCCCGAGATA"); auto rc = "TATCTCGGGGCGAACTAGCTTTCGATCGATCGTA"; REQUIRE(reverse(str) == rc); REQUIRE(reverse(reverse(str)) == str); } TEST_CASE("Filtering nucleotides") { REQUIRE(filter_nucl("") == ""); REQUIRE(filter_nucl("A") == "A"); REQUIRE(filter_nucl("C") == "C"); REQUIRE(filter_nucl("G") == "G"); REQUIRE(filter_nucl("T") == "T"); REQUIRE(filter_nucl("!") == ""); auto str = std::string("TACGATCGATCGAAAGCTAGTTCGCCCCGAGATA"); REQUIRE(filter_nucl(str) == str); REQUIRE(filter_nucl("tacgatc!gatc!gaa__agctagttcgcc#ccgagata") == str); } phylonium-1.6/test/catch.hpp000066400000000000000000024047431416332546600162060ustar00rootroot00000000000000/* * Catch v2.13.7 * Generated: 2021-07-28 20:29:27.753164 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED // start catch.hpp #define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MINOR 13 #define CATCH_VERSION_PATCH 7 #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ // Because REQUIREs trigger GCC's -Wparentheses, and because still // supported version of g++ have only buggy support for _Pragmas, // Wparentheses have to be suppressed globally. # pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL # define CATCH_CONFIG_ALL_PARTS #endif // In the impl file, we want to have access to all parts of the headers // Can also be used to sanely support PCHs #if defined(CATCH_CONFIG_ALL_PARTS) # define CATCH_CONFIG_EXTERNAL_INTERFACES # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif # if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) # define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER # endif #endif #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ # include # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) # define CATCH_PLATFORM_MAC # elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) # define CATCH_PLATFORM_IPHONE # endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) # define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // start catch_user_interfaces.h namespace Catch { unsigned int rngSeed(); } // end catch_user_interfaces.h // start catch_tag_alias_autoregistrar.h // start catch_common.h // start catch_compiler_capabilities.h // Detect a number of compiler features - by compiler // The following features are defined: // // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. #ifdef __cplusplus # if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) # define CATCH_CPP14_OR_GREATER # endif # if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CATCH_CPP17_OR_GREATER # endif #endif // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif #if defined(__clang__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) // As of this writing, IBM XL's implementation of __builtin_constant_p has a bug // which results in calls to destructors being emitted for each temporary, // without a matching initialization. In practice, this can result in something // like `std::string::~string` being called on an uninitialized value. // // For example, this code will likely segfault under IBM XL: // ``` // REQUIRE(std::string("12") + "34" == "1234") // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. # if !defined(__ibmxl__) && !defined(__CUDACC__) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ # endif # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Assume that non-Windows platforms support posix signals by default #if !defined(CATCH_PLATFORM_WINDOWS) #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// // Not all Windows environments support SEH properly #if defined(__MINGW32__) # define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif //////////////////////////////////////////////////////////////////////////////// // PS4 #if defined(__ORBIS__) # define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE #endif //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #if defined(_MSC_VER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) # define CATCH_CONFIG_COLOUR_NONE # else # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH # endif // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor # if !defined(__clang__) // Handle Clang masquerading for msvc # if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) # define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR # endif // MSVC_TRADITIONAL # endif // __clang__ #endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) // Enable async processing, as -pthread is specified or no additional linking is required # define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Check if we are compiled with -fno-exceptions or equivalent #if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) # define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif //////////////////////////////////////////////////////////////////////////////// // DJGPP #ifdef __DJGPP__ # define CATCH_INTERNAL_CONFIG_NO_WCHAR #endif // __DJGPP__ //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// // Use of __COUNTER__ is suppressed during code analysis in // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly // handled by it. // Otherwise all supported compilers support COUNTER macro, // but user still might want to turn it off #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// // RTX is a special version of Windows that is real time. // This means that it is detected as Windows, but does not provide // the same set of capabilities as real Windows does. #if defined(UNDER_RTSS) || defined(RTX64_BUILD) #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #define CATCH_INTERNAL_CONFIG_NO_ASYNC #define CATCH_CONFIG_COLOUR_NONE #endif #if !defined(_GLIBCXX_USE_C99_MATH_TR1) #define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER #endif // Various stdlib support checks that require __has_include #if defined(__has_include) // Check if string_view is available and usable #if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW #endif // Check if optional is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if byte is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # include # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) # define CATCH_INTERNAL_CONFIG_CPP17_BYTE # endif # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if variant is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # if defined(__clang__) && (__clang_major__ < 8) // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 // fix should be in clang 8, workaround in libstdc++ 8.2 # include # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) # define CATCH_CONFIG_NO_CPP17_VARIANT # else # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) # else # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT # endif // defined(__clang__) && (__clang_major__ < 8) # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. #if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif // This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. #if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) # define CATCH_CONFIG_WCHAR #endif #if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) # define CATCH_CONFIG_CPP11_TO_STRING #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) # define CATCH_CONFIG_CPP17_OPTIONAL #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) # define CATCH_CONFIG_CPP17_VARIANT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) # define CATCH_CONFIG_CPP17_BYTE #endif #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) # define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif #if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) # define CATCH_CONFIG_NEW_CAPTURE #endif #if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) # define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif #if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) # define CATCH_CONFIG_POLYFILL_ISNAN #endif #if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) # define CATCH_CONFIG_USE_ASYNC #endif #if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) # define CATCH_CONFIG_ANDROID_LOGWRITE #endif #if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) # define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif // Even if we do not think the compiler has that warning, we still have // to provide a macro that can be used by the code. #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... #if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) #define CATCH_CATCH_ALL if ((false)) #define CATCH_CATCH_ANON(type) if ((false)) #else #define CATCH_TRY try #define CATCH_CATCH_ALL catch (...) #define CATCH_CATCH_ANON(type) catch (type) #endif #if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif #include #include #include // We need a dummy global operator<< so we can bring it into Catch namespace later struct Catch_global_namespace_dummy {}; std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; protected: NonCopyable(); virtual ~NonCopyable(); }; struct SourceLineInfo { SourceLineInfo() = delete; SourceLineInfo( char const* _file, std::size_t _line ) noexcept : file( _file ), line( _line ) {} SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo( SourceLineInfo&& ) noexcept = default; SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // Bring in operator<< from global namespace into Catch namespace // This is necessary because the overload of operator<< above makes // lookup stop at namespace Catch using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO \ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) // end catch_common.h namespace Catch { struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h // start catch_interfaces_testcase.h #include namespace Catch { class TestSpec; struct ITestInvoker { virtual void invoke () const = 0; virtual ~ITestInvoker(); }; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } // end catch_interfaces_testcase.h // start catch_stringref.h #include #include #include #include namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, /// it may not be null terminated. class StringRef { public: using size_type = std::size_t; using const_iterator = const char*; private: static constexpr char const* const s_empty = ""; char const* m_start = s_empty; size_type m_size = 0; public: // construction constexpr StringRef() noexcept = default; StringRef( char const* rawChars ) noexcept; constexpr StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} StringRef( std::string const& stdString ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} explicit operator std::string() const { return std::string(m_start, m_size); } public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; auto operator != (StringRef const& other) const noexcept -> bool { return !(*this == other); } auto operator[] ( size_type index ) const noexcept -> char { assert(index < m_size); return m_start[index]; } public: // named queries constexpr auto empty() const noexcept -> bool { return m_size == 0; } constexpr auto size() const noexcept -> size_type { return m_size; } // Returns the current start pointer. If the StringRef is not // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches // Returns a substring of [start, start + length). // If start + length > size(), then the substring is [start, size()). // If start > size(), then the substring is empty. auto substr( size_type start, size_type length ) const noexcept -> StringRef; // Returns the current start pointer. May not be null-terminated. auto data() const noexcept -> char const*; constexpr auto isNullTerminated() const noexcept -> bool { return m_start[m_size] == '\0'; } public: // iterators constexpr const_iterator begin() const { return m_start; } constexpr const_iterator end() const { return m_start + m_size; } }; auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } } // namespace Catch constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { return Catch::StringRef( rawChars, size ); } // end catch_stringref.h // start catch_preprocessor.hpp #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ #define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) #ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ // MSVC needs more evaluations #define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) #define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) #else #define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) #endif #define CATCH_REC_END(...) #define CATCH_REC_OUT #define CATCH_EMPTY() #define CATCH_DEFER(id) id CATCH_EMPTY() #define CATCH_REC_GET_END2() 0, CATCH_REC_END #define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 #define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 #define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT #define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) #define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) #define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) #define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) #define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) // Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, // and passes userdata as the first parameter to each invocation, // e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) #define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) #define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) #else // MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) #define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif #define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ #define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif #define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) #define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) #define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) #define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) #define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) #define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) #define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define INTERNAL_CATCH_TYPE_GEN\ template struct TypeList {};\ template\ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ template class...> struct TemplateTypeList{};\ template class...Cs>\ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ template\ struct append;\ template\ struct rewrap;\ template class, typename...>\ struct create;\ template class, typename>\ struct convert;\ \ template \ struct append { using type = T; };\ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ template< template class L1, typename...E1, typename...Rest>\ struct append, TypeList, Rest...> { using type = L1; };\ \ template< template class Container, template class List, typename...elems>\ struct rewrap, List> { using type = TypeList>; };\ template< template class Container, template class List, class...Elems, typename...Elements>\ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ \ template