pax_global_header00006660000000000000000000000064135717017620014523gustar00rootroot0000000000000052 comment=f89184bc21413f3d30af7287b30edb8d6a087105 phylonium-1.2/000077500000000000000000000000001357170176200134115ustar00rootroot00000000000000phylonium-1.2/.clang-format000066400000000000000000000006621357170176200157700ustar00rootroot00000000000000BasedOnStyle: 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.2/.gitignore000066400000000000000000000006021357170176200153770ustar00rootroot00000000000000# 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.2/.travis.yml000066400000000000000000000010371357170176200155230ustar00rootroot00000000000000language: 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.2/Makefile.am000066400000000000000000000002721357170176200154460ustar00rootroot00000000000000SUBDIRS = 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.2/Readme.md000066400000000000000000000040131357170176200151260ustar00rootroot00000000000000# 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 two external libraries: [libdivsufsort](https://github.com/y-256/libdivsufsort) and the [GSL](https://www.gnu.org/software/gsl/). Both 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). # License Copyright © 2018 - 2019 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.2/configure.ac000066400000000000000000000046571357170176200157130ustar00rootroot00000000000000AC_INIT([phylonium], [1.2], [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]) AC_CHECK_LIB([gslcblas],[cblas_dgemm], [], [have_gsl=no]) AC_CHECK_LIB([gsl],[gsl_ran_binomial], [], [have_gsl=no]) AS_IF([test "x$have_gsl" = "xno"],[ AC_MSG_ERROR([Missing the Gnu Scientific Library.]) ]) # 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.2/documentation/000077500000000000000000000000001357170176200162625ustar00rootroot00000000000000phylonium-1.2/documentation/Makefile000066400000000000000000000005421357170176200177230ustar00rootroot00000000000000include ${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.2/documentation/eco29.names000066400000000000000000000020551357170176200202320ustar00rootroot00000000000000AE005174,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.2/documentation/etefmt.py000077500000000000000000000042661357170176200201330ustar00rootroot00000000000000#!/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.2/documentation/manual.pdf000066400000000000000000006207051357170176200202440ustar00rootroot00000000000000%PDF-1.5 % 21 0 obj << /Length 1602 /Filter /FlateDecode >> stream xڥWK6WR@".z&hAP SZmbeѱ3ʱcؠs8f/$gE!z!DJ, x"[ܷ?wn7Tr=unUbzkb^|[u+ ЩBgpS+˨^Y'u(pBx6e k*YNmi1=h7#>mYC)*"( Y"܄b޺c6ze-U[o7G>783mp)^S0v:tZ :PYC`B@d05ך'; )ec`@&]x z JNpJ_?,tlb$V'6=Jptӌݜfċ '[׌}H3nP&h`zC VH!s:z9rHXALH1I&TWmoE 8vhTyeLs<$X?Y&KCÓn f)ꙧb_֊)PR+* Cr6UCwvڧa\/!4[KI^ '?Z{*$Xsc!?a)!%C\:x?|`gd)vD=YM B G( aYf7dQ#T4XT`TQ!(.p(K*r|Y&IeZB &;7瀅hMI5Ēenx'8{.;6`|YaV,SuUi5ڮ%>}h*K0D#!TV4q߸6t "qx3]e 1ao-`)\ XR?h)}ݱVtWq¬q4%ћch@6Ў5lHD9=hNZt/2K)İѾ3sw/-bLU2~Bq9M7o@;MKotә(y9]3,-TӹZ_Q'p,V3rK!Y7j8 ~jFܦ O)pYC&+QQU*qj)L.oHi0 o*rCsi"/~QJ?n endstream endobj 41 0 obj << /Length 1741 /Filter /FlateDecode >> stream xڍˎ6 XQJJöI-luMDD$%n3ʯU=y8%zT, V .VK"%__0ϢXk۱W˿WbQXXq2̠z6Y,""1[O(ӜY2QVe4x*v*5 Y;;"ASHj=;Eʴ{/y",RV`Yy5gE`6V3RnCf$<(BIl~FYyet)w؝H7. b((Z[nT>g$J%qwU@ tkx?O/U=.O<@ (f?V+<.9f<~;X$,ԇ,J\)OzƓ`QI8b\trvDEdG1v'h#uEyְD6z]7K(3:f)x2~=|˞Sy:3x#Ռ͠L;VZx'%/] il ֓]{JKU9,-ҋoj^`Y§jD xuftzWj"=; ԓL6+56(lL%l$$0̋ؾqƎ`gW±Z4v>vW }ݟR6 {Uy}eDXj$!,ȜE $4%G>2/jM~d _"n%:) Qvx FZ),!ᆰpQTp;a9ï24k&^7Ǹ>0V~(WzAxid):/vla۹98lF.gY8,A.NQ̉KFkG„ڧi#)@dond;;g{nyw^VuU>0J!߻);ʹ{i1 oGҕ3ٚ"9p}6f}PDϽƅkZ;r 5uBlڍTX+q3Ӥ;3Y!flLa_ ro0? L5\+?OYB5u8:sJvrjՍZi\*t'I[}/\[w.<}WR &3eF&t8uPڠ׹7AtcKƜvG;ոio$ [Id`<)3Դ`Da?z=7J^շ}rwސZ T",YX՛73 endstream endobj 68 0 obj << /Length 1956 /Filter /FlateDecode >> stream xڍXm6΢&2bp5[m?hmV#%^oȡWdpf8|ȯo$d$S\MQLN4WDelraت&>k|!P 9 f3>uTfla$$2u9T|+(*ٴa-5 xӣM)Dgrkr[vb$D 6vm0Kp'Motuut._~1wQM U2DI&h"D\'f(mwb>'KWo?u~X[NY:fmvxg܇HJ5X(yi(6]P0y.Z(/o^ovV^ܼ_(>o+ų[y'\yMC#} 0OxƦGqZ|Ykf`OOVWyQ7(Hݢ^U/*Dp8 Skf6G)1p %#f01 6q'm1 2NEσ㋦&<=#o|Ofh y?s 8>&נJ6k^e}՞zOM8y EHh'zkw]TleqLW݇zr22 VW8vef>-ϒN_5(H0#( ), Dm4T-8aKE"߸_tu8 7a$p&BOlȆ 1 5}Pn@`Cr'~jaODvCöTe Q?sh :(>B?8HҮ ]ֶl] 7WԃJ^n _jW HUԳ1& _;/ru 9@_38{ΕO@3Vq.DkrUֿ]b)B۽_?[ "erh\'VF"haf6߽F0$rqʕyJ& {$~g:}fLRMa}S S|djw`Yo0ȥn4,Yp ~#m8.Jq`4>2#ƌƈ4lHJY_<)ߴ:Ob]@1 %u!!nG~P:>s t1:?-#!2:&ω$ endstream endobj 92 0 obj << /Length 724 /Filter /FlateDecode >> stream xmTMo0 WB[Z; ꝺĉKݯeمA`'"!C \cJJnczgƍWVeýz}ng7-GS:[=(d{# Kw A(ʳt˞r:%|)4$[Oފ{غE )&J=qSrSYir6e$Bf{ qT+`Rqtnt$mob(1|rΎ=Gmh!2lReI`k284婾BQ9n=.;U]NOutE5y/~ݖM ݛTW͐oyP4jl #hA`M9Oh8h C~BA" `Or 6B!D([8H.rkG iD֊*\;'J4&"RP)@LiPqJr5!9%F(C%U9&lk~.7a-]{n=h[zk3ֱVqmFj6~TM`,/5νHڶz-,3fT#) >z\@¤9@ؖ \Ͻk͐t*Doq{kg8xPP %-`WR={[Vyw1m+Y\r)ҡScbj endstream endobj 89 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./eco29.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 96 0 R /BBox [0 0 482 436] /Resources << /ColorSpace << /PCSp 97 0 R /CSp /DeviceRGB /CSpg /DeviceGray >>/ExtGState << /GSa 98 0 R >>/Pattern << >>/Font << /F7 99 0 R/F8 100 0 R>> /XObject << >>>> /Length 4586 /Filter /FlateDecode >> stream x]͏ _s[2PH@]"AWgkӱEQE㗿ww×u?֟?}=Cgn|~Wg]z0??wo_y~R:)K׿ɮi5M0^{ a{/{#B?R 8J k»@8+]oC b ;#E pNpPFD9n?~>ݙ/֞$ARm+SDE`z/|Do.Z%UywD F@Fbcn"qC%M`g^IlHJt`YPҩ? DdcU5ԍ2_8TҒzM%/=CdR$BOp IA} ҘdL}yZ䑔'U(w/Z2-iBs*a(8hE8zKfTK|6G'#g8EpQXS Cg]mɪtefhX^A:20Q :lb/b 5W FU2ASɩ4n 48X{I,.ΠfWnX* Sh%$ЃH:hSLJK$+ucwd=iam$=ҩQFl%(&L&`$ Cp&1u#}?z8TQ\][`éaRsp3SU]?:%z ?&ut8aCwƁ*$1>Wf1E ^~8=L po&]/[ 8m.QE}{ΆNoC:]o ~hP~w6~Q\XG&{s8pUޕHI+kG[HNpaä9oMy.6FHYgVSQ]3P] 7OiL+{z_ʞsH ?]5s}~~LS'OJ[̰;ִňXZ㒛-aLY_ZbKKb}m񱮫.-@nt5?|[|zih5〳j\Ec m<1'#(OВ17(9= W XJ-lيJʁ4S6verQ~L$b\S&psAyLTƋpπ6SEHpƗ߳a<F^94,a~@n:NބHWP:<\( 8pEL<(yg}Nlc6oLyN}EY:ΞX.'v[<ĻSߍ\z60 ? AGd7DBV_Fao&OU_`۠ ywcl8Ɲا=i:bhg@GPƺlِUP3 *RmX0-9 k0,a!)]i{ho}'| h`ez9YRC%SR!/<$<^;P˴U|8s/qgZ+sg;Osj< `|@H)֤ӢKkBYŇԀzt3R0-ӪD= j$ʆ.yXR wnwkSQWh{G[5{`GX 4~A8LnaEL;;Ts@H&WmmyR&l(ql)C {FY3eё_3P[[CiX~ڟ ǔ)_ k2w MqM-Ewr:G_8T[#,`cvc;!63bR"kyrsN:sN_MXwM Zo&;n&p ]iYkP5 #&u|V,n~s^{yMe_͍rxZؙ%y㢼yw2͟},pXP 3%)ui݁#z:k'$)pe^{Mf*9+;sr)$L<&:Ps<8/d~{tp+Ǒ.:>4ݫݜcHO T=kEFfJ)g0vwzp=rOeWGBFXu|yBR;cCzZɇҢI7jZ|Y%N95  g,#%'PRHI@3lP]N.ܚ3(aU|<.:Jq`kmJr8='f0;&f=ÃL-JcYñYuWu^tsR!=Tw8.Y?6p?TTsE`kU^FPC-WsZE Rx2Mޒ\|QWK=}5$u/ i5Ue|}|#+D ۡiUUu*')weK=z>D7Cz>O=2ܥ7>xYre>hLF,r*{!0V+{;܃nNcH*{ 770x߃%Jt#J]a@?/J9(T-b hxMH+#xt3Ro0^u*P,|>\C%m#C@G;12mdRU(_‚8:%<MW\5i}q` פ+؂&q>cta6g#yqs'| uVy8e%,Qy#Yȯ C+?½sLt/y͇hG>'~1Qlel8cɶxu9xkOŞ4h!=-C[-hXsm 4I= l r"%4S^&2zCZ/4;I#^m Kd K4)mUcshE=>5ɡ $Lf8Pc5]7$[BO+,Ř1S*>E=5ܱ7sj|sBkc1N;-O#HOv >q`o֐ˇ8YHcd2Cov7Dm`tʡFSޔ +-UgkkˎXˡ=C♅?RP3&3`5)9z%t%wTIo*oq6V!.tcCz:l1۱ uxBi0A*4l1=X(i|>7xm62 LUIEW"a,6CdB~^ClL9/ܑemCҒRj+AД.Cҳay9R :i Irړ$ob7IA/쐭EE`z/"GqJfw;=Q)$C]gsi1sT.7(ծQ^gbZ[C10۫2˘ 2;C-ߖGy endstream endobj 102 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> <004E> <0066> <0078> <0074> <002E> <0035> <0054> <004B> <0075> <004D> <0047> <0057> <0042> <0048> <004F> <003A> <006B> <004C> <0055> <0050> <0046> <005F> ] endbfrange endcmap CMapName currentdict /CMap defineresource pop end end endstream endobj 104 0 obj << /Length 385 >> 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> <0003> [<0030> <002E> <0032> ] endbfrange endcmap CMapName currentdict /CMap defineresource pop end end endstream endobj 107 0 obj << /Length1 20828 /Length 109 0 R /Filter /FlateDecode >> stream x\ xםot968ƔaXF$\- d Ɓ!G)&i Ii3醔m4m ˦XߛP[Io޼?~sBhR!d5N3kUrOxwai. FBܔ6ОMYׇzl/U\_zNGϰ0L?o~cIeלjk;BRV!Mf BO/3LRF _<ٱՈzBw6COf/""? rn2INNvрm9eF=?VoלC?s\ܜmfF,3:+#E?..Jg\]SF|낝sY>.o}_gGhO/=׮6On,s_o.ʜ܊n߽Ӹ'57P(=B!vi3P\  ӦN)Ԯ}x=o߿FӜ7NfGtuk/h~>ڣ͈nu߉q@ hJZoumڌ?bI@`9LJFhP)qi#%TJUۭfl7m[u//Ϯ5۾T6wwoSv(8t(zd툛Ƽ\swajw `nVK, (D.\:R 6MQw4lqz}f+޴qVV?3wM̂[JN0G= ?'Lg^6mø~ù=o#בCپ>}[O㌾>4A6!@PVbfX}i„lqx^fiǎ]eW6M ~ LZNܵqF< Uxe˺=LQ)W;/,N*Nݾ}r8wo(<3G_ugB:"yc:v#?^ij4Grrݻww=4[S]a]~|`!<-x#k.ڦ9ٙ8gR$XkN/LUŏ5 [m:5:O \@M[/bۃnt>$~MthZN.\,,h~jXp+n<Ñ5/l,ʤvJ39ռl?q0~lxL;~?.֊Mεkp!֭]W6}`_!Y iLFJuςŪ{f'xs翰Սٶ' xa+TyeG]a6͢oųf{hfbi^6z^z^ ׁ!`6R$j5}Zk}}fsm_ZUzHSS`S.\̖mo{Ğc|m܅ y쑋k(PUNBRzTm ʟJUڄ56uz[m +gVeݿͲef?ϯm'quFcU[TӳTc6Ԙ4YxizW$t~%O3c<}.[YIs3D-zYp]J{'dp4F*8LfCS4zb%xisxƌY]kԮ5ColzH/(@Xu5N<9m _m "qx;a*&% svJ+2X,O<]8zgx 7I \XurҽyrBlT jnoz܀&oͺ_49ZngKLwM{ޑ'Lsq'̫ãFє[n4Z=>܈+mEc H8Dyy0hXrIU,éFAPR);zjLfaO9S+W=%mpC5 1^8[`*k_nFzO‚ս5k8rv Xri̡{4'wwӪO%*7Zɮ0RvU}1ny ?/[ kMM8nvdτL n02tǎsx8N,.[CU 2N ?4ǩq/?oUzUZ ./!E`I 9zTs"# QfGCUer2y3'VG>E"S_4;MSWLqGqjj_Uxb8F&r袃ƨOOT@)ʘP݉43 JiCϜѕ bCmƖ;Ʒ: [g~P 2vA1he&C>/x<^m ObbP(9*6"8D<غȒ;Rqc=GUk-'uBCxۋD)lEoq.y*ƅPV0HuKtI'?8޼YfgkSssX~2N)Ж/PbFrNJn=3o.2xlB%AU9觰$IVpGXMubph/8,z'.T9{y+NS ҤgA]ǯ1;, ,m΢%K$c@SDT0$qI܍'%vRU'j 5?䞈K7rtv69dPF)mdlZ*3*o|x.~5⿯~8}Q$~|g6ADZ=>vl/9y=Ė-8cp@(e,y1ubq4y{Gŷğԟh0Vh<@ŇTjN UUēGtRx[=s$QŌk*f0nowmMv> yXr%w9z4C\qO$dH+F HoIOydUGVbrG^ލq_Cx]cψg߱y|m/-'N _ ΕI|IsEAf=S_zm?^\~mƀgtH$ 4P%2X)Ӛp?WjDK|cNñO 9iǸsNf`M6bQGi%^BGkLGo+p+ţpjN0&8K!Wr=jGIʨ-pV]!! K~)Y(xUq/r85M b"x;B韝RJ~5G3 ܍ހbY՟ M#5Yh3vi6#A;qzെ9\^.D taN3QbU5{H?@GP!ОECߪ>}_s/j?AGHH<c5LH`^BA~x< /@t˄1 .S*)L;UUO_S7ԟjkj^Dk_ôZVvk)i))ROSj~7R-I k4?BAREa$x~4 BCrSjMò)|?@{Bu4:F g,Sz!j ]@#R "!'Z?+$P|DK#~SO1G(̈́;] +L};HtG<"Iu9mXJw0JDHgѤe*{5:{KGa:CGnXB4|Z<摽AiHH20b?9:uY-.2O^/ɡ#{]Sr9aC֏QQ*azG #Iwds YpcBf4j\Iw+Ҹak-p7LJ<+p"I'622>:C(+%i#C]uG)ٚM ]F]LBA8JYGF5WZAN60+uq:)Dfu!E $H,NJOZ؏WlJ%-is q$8f? $Ub%XbHQTrd_Аj *ɶx[i%"!kTԕL"q'.ٓOCIR+^TP3i䢒{hM'%:HwuRde 6z c(tt2ٜY'D8]eFy//9jpZ!aNL;XTۜ/b4x$@1-8H¯T4vdVf!#fx~ <.Z x *\ 5V5Z6%H!yG2 vxo-&CUl,~Z1;T2P>d+Wv}3}UVPr?Fc E $Vx'a5@59@2 5A^GvL\LDPvi}DIR;ǹ&Kej zm:YSBJeB$2+2[FS%iTJ/bH$*ZMV.Q;}*~u]%IfLq*2g3vj=W*3R r:`fuE)qS݈"bRZu6a&@26덱TrkO-:٧d-q-$ 7US!PihֶXT%yX<iIGʺ-w+?d m14JuIs±\x7wrmb' etoк$լNb6t,J>-=%v.ڟK=J :Nz7^ӃI 9pvگRjQW:h@ Cy ^d^Ia3!EdAOϥ6PC"!j,~@BsYtdjeh5ArŚE|q}N7t& > v >b<(w&?PV^ P',v]rR¹RTas%v¹;R\/\ysĢz[ysU˟.IR'U;]bQЧ_{tڧL,K/ 2NϺ_)wO/씉 LvR j5VB[#vHYgG%gGܗvv^(~Jt?߳#%^\z^ÉO)͍$@K B2+89_6(~ˡb.!XW#Lq{`;EN?g<$CWS2.(iy"Afvz#."I'AGfD8$(lDD,uMCGxB`DĚd0+AGuc] !O7qBe ;g# 9~!9Z<@"*@ 0AfUqqBe[x5đg~|?\+Ȼ@*]8׃u\Ts ::B,aO**l"pPs"$Y`@sx& QS.Γ,Q'ē%I좄>LJ@%p98!l  X$K DF'&$ 9A/G'7$݁2q9\C? uqvqK8.*K4UD551, :VP `~6Jb Dn"TRio[ Z- Cuk0ګ,uvVXf{#gFn\ sjX9cMh9TWa4f>Ι5F;[V`#j *0F[i&g\j7יVZkF5͕Vb1@Rh5ά`&uݪ0譳tV.))g'mUzaV%4[j l\-fnT0$@rX*5D Y&%fޤlr#FNW[n1 `),tlCJF7bDi0 :No5ڈE*RI=$3KVݲ ڈ0&2,q0m9HӨ;uk$.<+!%,Zu/ؤKx7T"):xȀI% ɤ#H H5^`HU+^&L (V)t>A5A}䝔Ԍ!x^O)X[J{]{{%6+q+I}5Jz%{%6WJ ߤv 9$.r]W&KotuL mXe⮽ebLܵL-YZ&֮Uqͯ;bM쎸k!#z#Ik|6>gh|+7>U4>,m|{ЄiGBznBzvW@~5s[xaXiz Y-) y-$z/ endstream endobj 108 0 obj << /Length1 3844 /Length 110 0 R /Filter /FlateDecode >> stream xUOlWfwرvRyNloR]86v5nξٙxvfy^JBTBEB^@H .HQ.\8 q\8D=7o6MRif}[ބ,K9r~ m;COƏ -d>Op?a5ŭ}@u>2>uzYSݩJ ht2{ys^2 ;U]4[.MLhl|zrR)O*\/7~gjio\~W*3/]C#Ǐp{cc/~a.~? ?P'R/MÃU8yrmwr)rh.ڧ/VArs=Ж: L(,.+:}_Ut>/r_{HaAчRt_惂~8uE&: ZޥPRWtᯊbVt.Ӄ*:EEFC)g>Qt?TSaW @ٝb(E 6P%$-!~ȉ# -WF|_OtYWA3XGM,&XrHRjI<3`ZG&bs.lM,ͳT^5ԷHb|f| ȑuO2zH[`SՅ}8rk+|6&QڸotuVjM{*PGDI8BHC8O$ЄiRN |<ȼ9aa*ga{hQBȤ0OMĵܥl&k4l=_D5+"W"C ޤ!Y9|^ǟ56uꝼe! ʽtٌ{T{Y GN>2G%ԉe6u{-7uHհUلjcΉGS;Oj[Q9rB\S}]L춲=99IOewl5A2v|&s?OtjZdMGl#:<=-NF%7PByi>m^y2m:yMw$> stream xڵXK6W@.V IIԞm@uC-ӖPYt$:}g8h.pϫޤ|!RJ.Vۅ).r/j'Jqyk24<2,IwR@w;J*dLXBlunn>KyC}nm\#KFn]7 &.`>Al3Y5 *x0FU݄C?GΥل(tXBf,s, Et4;<,fp^;D);v \kc;vdDYM|/{~Q;up.HB$:: q>,Eܙψ2ˢwp2ZJӌ%E>Z1?~m98@éqU=g`\ȇa@d%}9Юxx"Lr"7Z+4;Y0wSMaf rV& S2Α${ >`[M+-y W5ltQdGZy)S>w'ʒ)9/ig\d qC ւ\r scd9YYЭލ,O gƼ0J%ȹHLrP#'y9"zaΤ K"3Nx6DX`n "H[U3hk)_9 ѷ'd N{Fr `xF;}}e;Ћ\O-`kW>\;lp=0dČjd44;Q L$L?c0MhwDEm,ָeO\L{40G#~gAcCƍ@ *:vBcCdoÁ&o"}ǚRU4 rR 35 ȞS1TV}ɥp6RFK-y@0QN3bnK5Z-Ra0AAkǂBuE{P)ݝ zL5H0mk\vST5M}'iIH=Xp_m y;AQ{;K>56X3 W[@2}">IdM= ְ3 $Q ,jeY>ó^eh|b}quM3 \y ~cW2l7 #fO SX,3B3C H*;TP`ktڅfﺍi-qr#+|_5jƖU$sz7PQZ/ONh]q `5p$De{b?!1l!G6jMjPq(2Ȅ[P_A;_[۶4]25_m4t,߯_߮Vsa9b޾}LxuWG*Kjz;a/K[[}l4L4> stream xڵXRG}7C8A(bM% k&f]i~O WoY'/u*Ird$yr@J"eK#.%6К%ZNd}DP0hr U!>BQЩ0B@Qyh%YIk(¤,(aׄ@ WJX@#xbc7%u6Ebt;-BΑj}0 T(wP#S6Jg/D8H @x,i 61+p8`Z'`\DaA 8l*"$`Ӓ0J< QFG([#0a4ChALxv:";f<%$0Rua%(Rx{ e7ȅLSߠ[py$ݥrBa{RO[zFmz7ˮ+~G&bZo?:PUٔ>,'dJli\REGE՘̺tX]/4tD;1Ey度=P[xY(eܒem}>t/fQ*i=ۡVz[k?,GbCOU+Qd͑.Y|ZV'3&y!mkQXLv!A Yz%rbV;Z)x˒UAD0ypM" h>O6UʓoEʓU b#`bN Z=JF{4nIꞼ||HFiMW壘rrs?͚OE.EoFoq9R2)ِ__b@|drEv0WLC@,w QH V-);Rvw{ Ih̿]w,/ݜi{U~uyyUu]Η_Qz|fM[Rh랖_LN2eDd19 uJCXdVdC]ن\iXs2WK)5ҖbjK1djbiu^[FlE %܇KEWc|}n^ֿLm@XU.6i݋[lwv{GpqMWV Hf}df*iKm3 zєgpt-,?~'F-n-a뢜V`kG ]TeZ~]ԛ1|1< jY?_o&U,i{λr|κ/;oP=rZ7U+QD0;pvt7Ű0l5zo~yL,j\Īٲ[|y{v3/Os6[W\QbvEg!}]q{8[]u(c?5qXP{~LauZPRqީ1hJ>/2}YS<$c-y ? `>rhlߔ?g.-IJ{iȝZWVUW˻}^cΕ^Oh.Brwנ4A*$e56ZXb@叻j>Kp&ӈHX{wntÆ Ekeu]~ i˜ټ0<v{{aO (.-1z p? ~+QnWY#hLD9k->꿩^Xk'B}uk.zݺ4D,7ۤ Q4S ЏKZ;}~_)/jC2tx/;T:sS!aٵ czaaTv:FV\Rcc á: I$rvLJsn endstream endobj 141 0 obj << /Length 1037 /Filter /FlateDecode >> stream xڭVϓ6 Hfmdzlfzx$LNlBx%'!$^w~)h%ۊUPJIEDT[KN6ʒuX|B,Z۴Qrh Q4DioQ'h7D^Idlq~tlE4`/h+e4gH y4U#X@hmeM& YjGC-trNSۛ;̽rw nnQV% ʡfl<&@gd>cwO 20<=>My Sh9PpƦ`l#=hi,h~)JP ٵPO4Oso]]f[cjr^[]իEҚ::p8q Oz endstream endobj 157 0 obj << /Length1 739 /Length2 36833 /Length3 0 /Length 37158 /Filter /FlateDecode >> stream x|w&ݶe6l]mW}]mvٶe۶mvٶƛwcb"#2+ʳOdR(2ٙ$lXyU\Mvf%G;#?!8b33 h / IMT^ED 4w/;b0s2G'-1? 4Ďc3;[k윁bW&:;;l~ףz89lmMf@[bg -_?fLvvDM\l'p[8S%+3 3ZFbakkbUNGY_h_wo3$t)M-o5T@oh Ps:KRYMlNNlQv̀;+d uV߼ze[a݉uYIЩ:;Y4fwE;wb/6bg?.[T/?Se;S ˴tZI1VHq6Q0S V2 ܽCHJW7Yt}ٸ`8p\eĕ$9JpD-n_B7COgF `ʝX3Fp=;%BqZ`:/^0k-e$V-F9?z5bqRNj…3T7B4}+?JCn.Rw' []~AΖr.lӶJbr/"s{Uq01560ǭ9ڹd4K?pes OM E3Y #3*k-}Ep/WHҶ,@"@%LWM{t3 $*s b}6P][6q"M+vx'\'MӪH![1#)0Hg`cy陴x&=T3''!+{74HiˤKY \ɳJw"I[kZkj02}pԢ [uVdXF}YNY{?nn{$ci_pбץP 0m!3$DE1+/]PM[|E3,dr04l1n 5XzꅡlX2EкR_)Ժ9Tߟu.t%03ɉꢉYskdbToS!ب +ޠ 5\^ Qb%X[^ ~}t~˲,c1F8"Fq/7OleId΂?̯{AȯE`ɺnʔ*wƧfONO8)Pm+ SpP`B50(JhA- fNHr7XwXZ[̚yޖ26UkR!jz?M٠db,UfV7ڕ%5WQsοRձ)&xB*~ٌ5&N 4?e7El|k8>na|( cTX|{E`(ڐ' ;Iw&~XYօ:JPn} hK?OTȁrhiD$<%Yk_Q?(ljDŽN"5iI:~$PE&ok9 mK59ׯs!;Nt_ocd>/ajn=7FӾ>Sғ|%BR="(+Ϭ* 3#^=f/he gV"g3Ʋɑ%-QNHo֮8Z?9Q#̇),[2nBVb&sVI,LsŽ1M~Pq2SKK\Ut 7 &cLt:5B,&Kg^Es+Pf _1_)[.(eV@Fb޹! f= X, b?#XL?MY>LɎCl揃=3\15l)}2{ +{\Mt:L ?zE"kF'{'v3gP$*9d_6Yw(B&t;0NOWn>ïa!k׻[/.|ɬS-{y%nO~r؂3iלz^?pohcNl:,&Ly:) 9>I҄B6b[,``dPݢ TGQY<"y]QAU fI 8Uh(A4A<- h1& N5 r|FґWo;132H' Zt[H|%XZrI#[\`JBr1fV(a4.uF- XD "s˂ x} +d+h Ww~k]Y>*k$\Wﻈ!" e &l; VE֞Х++`ѕ s^t;|]>?O(?Nob ^s=:퓦DCN39ݢL}>\j_X7c8$dd|PC6r\m:$=?=RF^hbGRyjk$F^Fj~4ֶ(Q鮹MW6`LZ.t:WO#vY~ ~G#t?+ۄGۤŸI=J~$wHx$"Qr~w-r?Z"6wzz"lFxQQ)RP@_S&A"]4lv6|ݗdR3c@G=X#'4jCыB;K0Q~_ Dn<Ҭ% P먳75ML9=C38+{NlG?Vj^h _w9+^(t}51V[<ìgvkXm!*Wi&KfeHd|o 9\2(i"-\SAJV/_i-HW\#2ǡf=Y1"&onMrSbnwˡ'zp5Ģ<4鉃5_]+yh$#SQ5]ՠ cT??HxlVT)&W2!{C!_Wx|{syiO%ǂ%ԚM(ôxv>kOB kiu m[%(|]Zl?J]GAM"ez\a&vR7M,l \>:NEGEk3iyKSɁ\"Խ֝~41\ Yq ш̆b:F1^H;RBNUߥL+w'FDĸJr4ƿaT"˲l=X6664< fKsK 1`6ה#Vvi3-gDr-*P] %: 8$:iNlfSĉ9َb Mp*Oʞ Rv|Ȁv 2'C($ {n=` ]󈶓vE L v#Eq?=83 ZΣh2-;u -/qKm)nAY]F—D{5<0KAdEaxxrHr!su5 8ę6z(zjCrr1C6 ҥ`Cy|qjfzmO&@|Ӥj _ Rhb+9-z],0k=ܘ3 bB Fz`[>X1wN=1 &4 b|w_}cA7 ӡ7#[3T,\S/7nL &&Bz>U5V6iuNsSUFidW"H6׆YA2v/X $B~ejF; NvX [vhxzPqفL,XD)@$;+`;-Ω+cAѡB㙗POtϘ3/EwӑXH) S*馸$!7Nިu'[ްc' %by-XԮJ㗜6>)8}H0nߧ!Ua8D- ʥJ}Ckξ/}D{nLrҗ'^AMF>=&ڵRZW]z&s%8j ̋BAXYmUE?h|cC|blXW>l\upYlxiڿ<"bW%5+_c '{{XSEo꾇~(EU" 8\2dƎZy };P;bNC\~حOIj6ap|n|ccJo6~t 뒩ohIq[ Yl}]N7#Gt1wP|}̯J1RH{%(U-{Au}S?&MV8ѻN |*kJh'V[`,j jZ?+v1I}>wC_I(!@H7-zkC05.21Ccn wURyyE{f-ETT.pS麂B4l6T&1Y gϕoV" $Ƥywo"yf<cV rxSq+00va٪[_~0cM3tpk (&%{́E(O-]#4n%CPR' |dcMFmFxzɎGd$fS j^b\M,U;!vlwՐ_D1HI݅"ޢ 7%,\ܩh-mH}FW'c82}eڦ;ɋN{ZvwA²M4!3{@Y'#j9(q"i,v|FҚ4Rk2hnJfbFxd's++h>eD2 wb PF;$nzi(֭~RQF<64.DzуXDOAV2ZinSjD.0\ӭz w={O#Fr06Ȝ6޼nTxEOˠ?i̿i7Ew]a x;]Gbv4IXb^vkB ٗFϿ29^-Xb('S.{XalB7YU xP]?K^I#q*h׵ByIbJ`*8YRDIu sBik08fcf`*$I@y Ejx)'!,:]w|GZ]G{K) R^8V.3YK-R[кW|eI"1(嘸o !1YHLщix0nOGým&bޯZKi{Ġ,9d5JVA?![RM xX P,W=2-n꽟 P :,:TRbl뒭>7XzNtO[̣b N\ދ][gx1^v/\-J+6fJAB@oџLQrV\6hTbs z76[ bV|RSNZ nƮ2·qWp%Eq/]iǫS);֕! M}_Qڅ;ǣ#?<3yjG|=у4ETÔ?J W}e߫|Nqy&s}7E/} 0@ p]1 [v΂WrWzyĦ3RW2K !TrGsOC+I#p ;B5R<-PY~AAQ|jNF n?`VpyI$?uY_gt8&I rR}_ f"Av#`Qťps6ّ壂#hČ/ԓu*3ʿcFB~$XM_^Y#6ZHFsxլocZ4;qcvO5^3|M I~*$u1+%&N(JAOh(R-RG<$uF0o]S$kR,΃y|%eWN+]ʵ/~Uh4ȇAm$@}"_I\={0Xc|H'KV #WD>SBFi76jEK2pJB]}2`#x'܇#>ć?Z2PF ? Ao8g Sȕ{NQ=Ȯ12%[)17؎aGA:,n76o- G2sWkvs&0Ɨ|)9,4\`NQW2Ǐ|ujAE--?9fa_vnO86kfٿ҇75 1KѷM.=)[Q > ؍V.Wng2SԌRqH YeQZOIᒹ޹&yeRH =$+ _kcJhuf[lÉvX2@!%QBQfx-/#e/dzl<͛=n |<%zlz)Qi\OO(݈[53AX{WYwcW O畮ޠcf]lj9AV23Q?zffَi7KֲġѭE(Y]%1wdB9/x4y- 1}`)nYR SZUx]dx@ |\z\]KZF; ,kڒ!ip_RxJ2q-0(6B&V)f֪-̣o\U|RBTmauT^w%$Y9T`0# rci]#uqwM 2yI͎ZTb&?UG[:byY8tyP$Sf B` /g!T]һ}%H,c2A!F8;Tɯ%63( me /$5*]\8t(fV~c|X|]AoBn|Vf4ޗʺamkݔPd#ARslo@Ew1Wnkv9)DlmzuɏʰO' ,yȭW:5 fyPȰ$h?xx`[ӑ2W+?A L+t{퉡}'{2A BWCiV|V)ʹGFIBȕ4ȂL^YA  XqDG.BƄcy/ 'Ռyܴ{-fE`7٨>.n%I .҂̇*fxCyIU׏+IPKTAg+.`ՎjzoM=`#lJFxBk c8#=tx }{?) l#nK6&w,K*Zj̐3ƜCLđ61Rz:.\EŁ'AƵ= ?2=d KΤ~ =UlGP *wώ:łW=1y7`Z_{ -@wgx]z8߃qVBQM=J&p-0jpU+D'裮 `[Ux oy^ Dz"9+I3|*˲U3e ׍9~Hkо!]e/e;Kl(‡ ka@7+q&5|5\;wƅoLoѰ9geڙj򏘭wuaBBRg7;a=9ug0جTFp]-FT=DLݦxrûwocn݊~UĂv}.?TBŏOY1f*& (kGi !g[}VbwT%.jTâX{: uDe -}X_Y CPpYwzIH֣ WqM<0bjΝ}HpƎ(5v}Ls5{%]I-fN m§]Z4,MjLa[gKtޞ# v9]p+1|;+F].V]_&`Be_1KLs"iWl{aK#CJ[$+ 1m-7t+acԱkv <]B` *XƎ@}.ȏސi5fIzX%r׾`#~k,[uy[+ Tx8 ̀^H(/IZa ֫e[)MXf)ExxwB엗v.` K ^oV&+!hHb[>,t4ڒ߉v5yli <L˟bKQ=9o5$@thq*$0Bh(֗li?Vq> Vp9J]׹N|.}&Ц1ƈpc4% ^Ð8!Č:)~3Sg^_4KfA",^2-̱L(ChjYC>r~U ubǢH^#7'+tOI{ϧǏ ڌj쐖E> N#l{H a%ivlK( TB+E-1ll誷 eoAWG/f}BUr/?9xEQv~մ\/jExXUWTɒD/xLH޾\|>Ek E==6R&oZl{q%nkOPS j8n| fɮ\EU4Y!~?lWCtjsF(R/$MXފaA$@gVChENϜcQ=qB`AQZWH4Tk).AzUwהXWޱ$7홛~\;BH Ox߷HT`xXHP CΓct2֣ h梑-jQk$_CHM%{2\v 71eL0P=_ev(q<qCG\Uy5SiG:.z[ۙx%o,vt3g;|$mM{`Bׯ*FFwi{ZtN1,M!|v`XxwfӺxyGY+n*GLPo1~ Df tR=,\Xc0$kD,-! s1t0nQ'ZB)٠YH,{% zO2D(#J%(d@BDj`bbG/n[CGPS\,pbJy|S4"=#m +97?uͶ6_Ljud0͵@'L}Uxe1 rW5/@˩Ҧٟt 5u{am`9ɰ XEb3 iT轮0Uw!_YvsM5E 09S@2|zh`yEAyrJ7fK*]<1vdo+Do( )nh BתK8LX@8 hc0lnDKV 1k H2ha et|cޯ"/?)Yv_z{#r? iܓ~]Y*Ҵ~8p8CgTȕna8Λ-8'^kKm5,ڧdOFlsL]MuNC(m\`gM+2j2vf֯XM/2A)mrY*/-Nryi(dr YwN%(ۘKޯU]mC\}:Xﺦ1t]+~*06wJ50-?%mn%8V @EG)щ` ! o}w5%M#yK( $ocT~RqC: ÐׅV_iGif1YԏgE8ŗ~m9JycS̱ q*HA8U}m7珪TqP1pM׹ۉ^OAIbD>2jmyfX)dcY d"W3Jbydu=hcJU&BzbBIhHE@<_ڂ<67Hy|A^?j=&8tVj7`}3[F#YV>  3)GՏ|#ݩ3 #hR",KЀR=~g̯6#! VOL1e{f4Xdy bpdl8<?A84+m .EC|yIɬU9b&L uQ]s#s /Lv0UVNY;\!{rf+NU) $>N',ncR]XGߒ#@9ZFifT0g -pZ\ Rc;OZհ/pƸ@sGn cxpXhCSa߁w-N-׹m*%OT;E3 RoHq t>>۸`GgG_!@.唐%c#X{4d74=FqoF09BLQT *9005 ȧ_qpla/o2nTLcGQ2{EҚw _QUboHC<_)@i[T/DU4 0rPW ^3\(_Tb=h~}a\:忝"Śb0 RRd,PB hbگ-pM[β.ڬE^JKYզ;Ȍ0{]]߭htW?>j6 SS^'  ZZ[2h(A=⾒숮=t/6}yWՊ0Lb(>:d5g˯h4':<_d!&wW*e{N2˙' < nVz4vɑ'P-"D9P# ڠ>#+ " 5}fЀ7(X֒6ҦF=+C\}H68f&sXuGdʪ-L=}@^ZHUnhO13IٲBlK2 ۬io6{x'"h0}.uSmd^1̽Fs\lw9{Jև8P"h&Hd9_Ԍ;QyAjڦC4.-#>]! c\Ln}w1cEycP& <Me6RUOh40;ߺh'J6seQ wgM ː+''@V8R,E6.*;%]=^.2 K7[]aN.A(`tC"rX O4#+Ǩ$ӒK .k$dd뚚l҈b "\Z'ќ>-xxXnh<+Gpr^ +n(xn"k 2&v5@\Ӝs4Mb$%!5 )}`>Lt~hܗW/7&be/ϠbjQWk–Ot6kBb 䊮ʵq6br~M렄_jw(7)ݏpe"dƒ,0K7H YCa7>gGsDmtl۶m۶m۶m۶ѱmuɼ|9nTuU( >tcx#lMh45/ 3R6LnX]H=|1jW`onUx&Vtkۓ\ ,(q`0Wn-Ҙ0)=UskBDmH,eYr. S&b "NV_OE\!!thb>?b\p?İ5̨QP!$G@L +ʬ*)x|ȵґ R #]f<[[k>Fq<&Ɣ493 \R6өԷ ߱oEvgϬ;MZ\b1HL>U~ [](DX$N- ˏL);6P'ٰu\3@; Jv@RxRSZΦ*tt ow  |l&a`87&/.lav\spA_&yE`42o:1PU 1dǯ [:f㋃aTdqf"vc,·4xC1Knyevw]ķ_gMUT[mXaoPs/v`緛V\ y14q M$7=[&rC̓cμH%Lm1R,+i-o/s t:aiq0-G5FĄbGǜ0ê2gL-%qF5l 22?cV1 /<6?!-YhE! k!P2/^ԛ-B{^^bcb7G{ܭx*7(8lM<m)'y1^">Y|j%\Bމ%bL4O]ha^#@u+' ӏV`!RUy}8JKಖrĨ,DA?Caz/[I@4hizsdU^G < l<{,]f΢;7$~èEwcZSĄkI7ѡ}'Tku) /+*[&#zN~LBҿrKf?KЯ/GZۮެK2&q$rM%FX{nsdmA`Bjq  l: ,|stmHe#`ad_zN]z:N/M4J2_6h fƮh(bOq;cZ9?M3:Dw9j|YIql,r%PC)`v+^,%JBeA.@tJ8+t.v7{ym0exV1nnO[)=|P/mXPy*]:x9IA~A5w=)TƁyZt l!ϫr56a BS^p^'_,#[aqlbL!')i1*浓p(3DrI3nvn~UKxA'Ffs̭PmJHjj:ۨd)uzYQpU=4zDַ,QRp^k<ywud0_6Ā&=EM_=lv`V~cM\i* LE3o› V:>}.8 4ӱi-U ݯV`ra/3 3W`<ck aόS kS3և@]띚z0s| bI*z+԰(,+1@uՄZ[vCÓ*e;Pzע +ϫY+~`8e:A jr# QAtZ"=6@΄#맛l,RȆVS}mQswȗݫdh5ڽ8$WaScMKKMLSCw.Я%r=ͼAw>G:?oH*ߪzikkx.Vc^ڦ&u6 x=)js' EC=0% kxPg=IW K Sq > ]d03y{N~kӄ/c9!BXlGH6 sAOTӴna-# 76z'rYQTm̚&=KH9|뎒p4YjAQ Wvo߉ba퇎327@ETgu}WYH#=Q2K]%R0$P˶NX bޮ<~GŽV6p3!H5zޏ1&Zd^Swh8ΨYka^~чeW*+/g&Ϫ-WOXؗ3I`m}&#g 3f׵M8'5ЙAwr*z3riQ -_7|(kMV/,S߁qzf=: >lY%$yjx6nϚz$<@o7 /PTkKG9nl)iYj'WW$'? R|6/tZsyfRLJm3x./S-%a = L׫w?}\}P-F?֯<ʉgpڔУN&A{Q|h _6l QǀWJrع_1 H=}fvMײc:- Ϩ k\chі+c++%>7^{Չ ecOאXm 514y5cKM۰c̆^) Lj8o8?YE_["e=G}6A–D' yprmBld$uA b|FZdvܟ5Zg7? {v%iKTycN L!d*%Djt&[+{ݱs. o\\zgnĪU zan^uCgĈqu;UӦU!I]>dfF *-e(oBU$c% ,SMP;TAxEbZ+q Ih_O ,WUzZU. &Pn##¾,4Z\r鞼V cQs57cWi7_bk8џ~&Ŕ$ rrÈşcbq[$IP9Ot'QFU j!8 # W ғ[;2[õ ϱ7S3p GF4SK[>ꛮ+;cZݾj8~>DK}(#*YTS[!,7XM^7lEh\P8xs!s_7H5 Y򮋝ܭCAHޞs|o(3L ]>RM|rSS&?ԙe_O ~rCnEbH|KB}pbv&zoHq߯DT m$>d(_|dJ{;(CE+%rq QʥY*v}'O}VsAf3wKwpjg!I|"]F(1d M) c)7ng?&[?.l3(PUW<&!xRr k! g`צP`. }XS"Gnf4gm ŽMt}P,<5$F(#ai:qp ?mje'K8uW2V4 \p"Cvn(L9*jICJs^2X aBy O猃 @̧4vZz?vPĹg\:]9G:]7{rCq8T啦ׅ,R,ML-l+ [x-* nWZ_qSم^SG-h/}wèmn$!?^[ZN5gؔ9.~V澻4q^ 3bWߛF}lw(X* |f|m.XHjL^3z1 $8B8 ,޿to*trV*= A-> K@~_6y=^i륏1 m`f tU|-Vc q`Xat\,<<)"k8<#a24 >j{UKg},!ގ ٺi} N*T^}i!ף6Nv`tĝa( I VK*-X]3xBLް"į s[n j?*aE,4ӎ{W;~ dΡmeZ267lUC|$P~ ʄ& ۊ˖b}doZ,Yaxq'5va EyQ>}lV;v,&STuȰ#b;Q,ǝPotHwo#R'1jj~3Ӆ3 QbʂX_쨛݇OZ .PŹk n|m91;[ \G`8ҒsO7uS^sZg- x}:p V1{4MV6`sn}LT 4coF5Gڴ"?v F)uWOL㩧bmP{ yXdXbX6:+aVZuhA;fZn+_IthoX 2m뚠!)OTCXV&=ҨW182ZmEMyWl4&^8I`yDR9N. ]`SnJfDBtJ״;b!h7¿^3at{/r YON;ɩn2[42KL辻,!TfDJ`:2} elF FJ&(iXQH@]!hbfqe^߫@qzٛ*\b7-h(LoyFOW(Y<#RFBO<K C(6;sM0h*(Y ̂.}~ ~Ur&umR{LTk$v[KTKpϓro>6Ap:_t-e01eaq(xq{zp &I[)\\щv^ ,^d@ȹ.W} +ɤp6>t6d$q`1l nIU-1W46kN}%U3e7OMt3Xj4`<&hpѩ:qd[ 85Eܡo.~2K-1hçʣ<쀩/e(LdѿeV -*Ee e.2.FËFWq-u F(Pا(5[i#[IP-Zs :LEP#$& 0zѶz̶vAFwib9IFCVA%+be٪4k:x{D Z!)ZeXulswLTZQF55c?O] y&'OR!7/:qel^s/?2<@ 7ut5YdN+yf TJKnΧw /䆥8yxC`N)}tpfvI lH0y}(ےhQ߯5Sr_7Ӫ>EXo؝8˸7Cc;V!kuf&X t=^U*}Z*}PS2UHW_'c^p.[ǒ*s->yJNwQn죠-P_Vw([ 8jsޞEDhrqy>wz>wf&s"\FWYqAӎ9-Gq/ʕB$TovwDs} 94J|LꋗVԲVn*pZd¾]0vWpѕP%rO̦  it‡ m+& aZ8i^e~ZI`zO3ذpJu`*MTgr/@ j?TTv=[c,bF?tee1yDQΒ<"A+t;'˔H /:0!3JzG ]dhM"oٿM@G|8Eb(+`|$5HE f9Y&Cr옪]!{͒V~E7k C1 A.8B9`Q#U PCcj)};YFṬ{hڀ\97WP1'z2gɗ -(ʕ3nL9Vn(~>,#B/(6iSDWYrJ4ʩ!]_n491_qT[ Fp9נCKV3Wx4rcG_YlubM=ܛbnZSeay!7ca; q|!!~/Z?2G@}t `BѫJP/DdbwmLq\ѻ;e!aW 7g]Gzܝ9Fֲf ;AAҰK4UƂ& {G,'_Mr$J`mtwOu)eቢy+Y84.UJ9`c.ƞOODZQsb猡`4_]:2r3]\- L#2.VV$F%Lp\.3AO+0kmUb̸~|J2iPnl=H=p+o{od%gMgR\ AH䌝hE4&R B^aeDOvƲ(Mto{|~N3|z')P4H驰XA,SKf^aA/4zZ2yЍo465 f`&ð#ȴ-RTWd&*Tru^C7S]]M ˫Ŗ,?Mo,X/31r:^9c=%pb5&!Ah(Au . ݙ }5 ,a\X"27';#ցiXyUY,;!ph8à? 217lAcΗZK1 :-SyzxLyJkB-A4U3e𠬉aUF>!W_GplD~1Dc&/7C? =;(F~Ⱥ1l0| |-In^\CZc2]hv]Pk0t(!#ړ93xXOApJ4hG"9yQ7A"lg2M ow#G B]7V:=DT\<)/D&=U&$Ea|.]@5_؅8(T!$=?I AɮqGKK1NRc/g̥Dr>Phֹ륑3.#r#{'q )3:YZWY=r{V(].drRJ[8:!+W]+ӈ.μ_!7A's⢢Ћsl1?JG.%PJH{8^k=fx襩H~g]Y;*Pi&; U 3s1 @_rAdA,S&5u5ьH#TܯQ&_T$j χc>ͰGl[fΝtːPWz;KؓLe gu=,ѳOZ?0.V$J;kn |-0&c&tAXW<_uarwn д7)Q[}-_UkNPqIF;Ne0*"vbIdZY3L\!R,gl1{\nZ_5&`#/pamtAϝ_ܫ8kvw^ݐKxrl]$fs}y@k, ,J*6>OJpr+JsɢA*[˧jбk|y9bpF/KSX+ʾ`}{v!4#[G$0X݉ 3_]CYg,WluN wzA]Ɠ cl"ps羰0y4Ca9*j7\dqwJ@:Tӏc~B]j8$P9,~&{K0A{OMagT&F,7_k`B`TLy=CP ևzo̥ksO nAi`*k4/&P3Wצ8cp7:]Yq]25{+{5hfq %bUΔ.=6Ÿ@d'WGu)꽆RWCqn"gA 0<SYPg`;,apr8âқzC{[yUU.Nz Ckx/jG]̀S.sQՍ.dGu/w=TP1˪g_&XK`cF|Y#a l?d c9A rՇ1Ap<:bLnD6U@nMe0#0;"QGqaK}chӾg ("9PM>h"&Qq&5:{P.;/Cx+b=k@D;.HYʼn+Wz`s̕HbQv@qX?kEo ?qzsBLKǖ 2)!/$h6_[.5!{J%^ZsU 7f/EQ!`vfDvmkx,DYb[AfKR# R2WBr:TPc >^2?DgzJ̷7_zFUDjp-`0Kp \|@biߝ ?Wi@;MoFpÚP]֡6uw%>o)lj8VbU|f\t[84KwiN%O.漕fihMx)5' " OP2m'_3#Lq\ĝ*PT`RD3l\b|h\*rRV8We M|6K if2W6(j?N7B͸4w=]OqbZqzvҎt(_bTMnNǻf9|R*?x~ -)6^F,ժXD&ї.BUmҽ0rh*='Ц}S>4}rY0!3f]*L.ˡ0'᤯LsR=류J U\flx0N1zVx,措P |*-֪ =G;u@Vl*{DA.I#[9 {gܲ"v=7nTea<)<,T1LT#H|lSɧjaqE(W9cZi}pI@ GȺaa P0\= 쬅B9b#C2F_cuN;W,.B5ݗ[=6k^K1FϿQz4S|}c5nMȯg)*F;yJ x/ C 1D]ր@ޫ#ʌKg΂%@l9tqV#3+C2Ȥ 虿奏L<D#tKw2\t%@a4Ƕ ?CyaaC-XB9AJ9^(ąxfcjVY8A:^cM2)I/2qP C^P N^X\ ' 9%D,l3j ùВ $r < ~Q(F)¦t`;vnOwDvĥUUi|'Y^ݼ8+[>t>+P;" |&Ts/ ih)a{YYbG'=Dlav|/Rk&..O-ᝃ#*AoS Vº,a$]5Va[ GKW5EJIT"Z 0h"g5b$xj#馒1ᭇ.ˣ&%(˛θNl\{"/}A3d}((Ok@ΆIƲ7 $&> P`'"C* = ᒨ@,fnߩZ]ts9^"u:C! <{83j X*Hz %Y_9RPOq=X&GGU>49u\{=@Qo)oC^H AV? fE=]@N\9yŸKH>jqoQ:pFL`:Av/9w`n)rky =o*3#i 1^2:Br{zNvN+ wIb!d@ LO3($ ^z)>2.6$"( -'$fqxC{O㲧ܒkbr7_Ŕ%"'ޞ#YMit<ԛ, u]@ʿ0._*F(nY\YfJ[V7hF Ch lK`+Lh<(2ͦx],s=9,Yl< ld0Tz+kqkX#K8ȼQ%Z5C ,ƚSIk{=lG/mC K u^Dx&,_\Jl9@_+6F_f6RaP gU)3qdFwMer\^`=wg"0$vLT1S/);,bs\;Z|1y꨻Z a;n%4Uc,CӎveN4G?s;rK(>vu.^Vy]²xD YW=Ղ-/-Z4(tM?/FKX`,۽~ ɐq7! n jG?R#u"WPۏȚBr=mH:GVat̔ޕ*5,߂8sb騲P F.7=j%s-5 L0i牰 ޟ.AߏmO{5xMP%/ Tt+8L.M}HP^ ;cv6(wG)B?tʷhb\` so_K4]EvG{G`Mi\ ЫgCdAR2:NQ4trZX14 :">q&`B-`/E?Uy@邃.dihDԋ,gZBj  8zl~m'E Qͬ8TΣLlobdFYr[?KcMՏqbC&AQh>wϊv}5'( I[8E=H +QGW(IusuF.Bb34C0i Wq;V˂,p֌ wdйh1o"IX#/GvԜ` .abƍJ;L$y1 Ϫמ[ߋzy2 J? i? wLW)["-jctˍSu4l]?A9},MĮ/Lb ;AK_ScT/^x$ZHyCHGˣ%mW]BkIWڹ1iLB'Y#Ik` L#[63dphb;  R'JT ~,W[t}6=zμ Y\&iKHs&8Tah[be[\*PFJB#  j 3rdGHj1 Fj4oX~W;@!fpHfSbcSf(m2#7smsl+g>sS򵲫JkgP<`h8_ca R>W<9elHPn_uQR(1bXV &J'U3pXltmu9эt۳G61PG乌?!QDşp~!vL|f+''wxE tz +΂+CtllI`j Tyy Yb^": cle Ajg$CzĄMU 8=utgH$mA LVEN :q rV,ŐUd['(ydRǁi_|em$mҾo EpYHwxJ+! ԛwOibзܪl">D̙\'Xʏ_BE*z&kPB%S/g5&6sx{̿§׿,=>h+k!t z߂|9qyU?Cbo])He:|t9[u uIٹTbR6k ~Ҟ5Qzn0$C5W]>\ Yr{%g,x>}.;u3(]Rt*,UKV{ct NszcmC3[hÖ#xjEς@\k)AS֪B{93qG6~i{N"@@Lys 5 aX1#"QDŏJWˮ+QYB1c9:zO}ɀMd?,k}4iFg5:Zm|DJКqd3 ][8 r+7%Cc?_\Gh.pHnm]kDD qLqAX8 V TP#>T0~lԱ2w\5T6fR!ׄf鑹mI 9T6dEFEVll}`Ӥ;HFBJ@rV ZKV9fMyu;%;ajhxL\l48\Kf]aWKUc DE{YD*0UPiʋꮣpo0s;1BSxQ՟6`z RTBL "2O;6zR==9T{(pk06Bpy^U$7)O9h"\ vjy!XcK@Z՞xpB6z<1ReŋO]ڨb5V*c|4ѶhAOkm=h2Gc=x〒ˉWgo9J_mO%Hf_Clq.qpGxHs _&'k~ҿ ă G'HS@㰧uP) wí-Zng!R4-_D?T\k;L *!ͫq%d\ ".'tN:Ԁ?8ycky0 OV}IZv_,mxl!{2J*mg3uM!晳{%2T@Ey3i5dncq:!3L~xꞪD" fœ ?y5O1yAp,?~/-ѹ.+uPȚT6S`I#F94*OOmY!o2te~܅SN/\lܭ[ S'T|rAG@B0-LMQPawZO>5XKi@m)JSYEo ,`m@CQK ^Xn+,7ֻ:SwQq3TG/־X7HIf\Ԉ"*\5IJH@A=bFO;_PsV#XAC:1~5BPxBPz5/$$0榶p2PUZS =S6w+|X b߂N6|B$6}3_G)XkrN{>cx ~!i퓐rD^ŅteўVx>rotW[nO*xdM g]񉭄`ƫjc癕tWw8f zt11I4V=,mQWZ]jHx)^WP1W>Iw#=.^A4v1%uw.;+=os+ơ0埰!*]D#KHT,Yk5fNP!XFH򡄼 V%b + 3y4)`/("L4}h5 V(-*NW lk[Pc&I8bθTfU=p!;}5&oÉ"' ִ݉eb|/L}_\ ^/il\m<9-n8croWkw9:T>Xi FknaV)gX)4pTx8Egxe4 x%7F;%hpfp{6MLoeiu9yPqSܜ^ f"ok M uh*8H'1=,yc7Z}ĉ&.1r2({rİq 9~f[ho@QG+A&Z#b7[SLtl3-1]5 ߳g|xK&De؊諙bpWs¢JXXcCcd;$0ky۠K-! qd)AZ霾i|s[s.5 P̪<_d~O~t!Ox]NQ+)H( s} b,nnqoTfFe]9jj!CK;db;&"1=2TZdDzxx7LP>hlf-8q[H\. Jtd3.4y@3P2 blo@]",U6ZR.a-fg TJ>\]85rVU|;0AllSJm6r.96ծA&iP w6f8 & qE F V0(87punE E5F5#W2 %l@V o,RB\-MnuOj(bYZq Q/E. ((X9%EUwZEK` !e#2oAG>dGx)b6]e}ٯ#.Cwс^c1]цۛ@3jz_-%u(b 4݃+ƀjDsXDUnk녞`)-glB!U׭nLy*V oχs<6`DFz}\C( e^v^J1ad haw[%}:@w/wMHAF "H{٩]s3 +Kz:@ aYlf/>K0G]1,x6c GOY USo]7OJ{V=P&pح+sj^>WrSrHʬؖHNTl{?ܒX; Wp!~6a}zGlt0 Ojl,JtvW;g4A@ \f4gtw(%+e{2y>Ɠe_[U[׋|Ӥ"R(n _N %kblfv HdBP'XFOfb٫ %WՐ8ȁny wO*VL ;#ØSa/YPUd£kF. @DBa19K0s Ti[9ӣapPӈ ѠǼ_lrB v6qYTsY۲.`0fm]k܅b}<24tzAqiu> stream x|fݲ-Zʶ.ۮWm۶.tٶmw{Ɗ13sd̑kX$J "@ {;Ff^ PYɞA 04q-W0tDդeLfJۙo_1܀NΖvFv6qSw4` 08mfuHۙ;9;Mv @_?LX&v.N@g oYY{e%ףpqqebrwwg4ޖInbP:)OTHZx_D`? OT`fd ^D`JdciTwtWXM hg?RS35r2_,\<WoFv#5P_EE= \6N+?&NN@;_?@%{LI16Hq61SKZ`c%*R{ xѼRJ77YL}cلP8=8JO@ jRܫaE"qŗ !CS,X=r d\mvG9@8!]6YrhKࢗY+oԯ&}PPíZ r3UxI96xOŗ6&k65"U%2;9J\doR֮ Q 46w-Œ*(rN׻ob@. jrܼ4V?,#:Xד͛RV":P 7fYt|w\&hUo_'OmwDh3`H$uYMIROo.!8qz)SX]< EݒI H\3 Iecn9GNh^ BM)&:pp׉ˁ|P=lWHDwVz'IYjߏPiNQx2:\;}8 T*i^)RY4? Sw2S.[k.qf,E>GwhH^yma:Xu5xT/`wzO-X\'ǂEkW{L;4sr |ck!`/?F.uLY8,A;1YEUGkPQ:W~Q;*Q&?,`؞G4DT/"| X}ןiE (sjv^Ɯ@SL^-d 3r69 b!aв|T*ώ;!t.;63LW<S;d*4{#;(2]-q[Y H/ m~@DUc/W PV& SR}^ ooHbqcFkD#v`fNe@b(HzOY?,C[z;Ns#.ۑq[J`[E DU%xOWUw־.vř4 cf4rme˵P˯X wDdfVA(jUk2LAp A-ˣV!pl$0OByЎ 1P_48&wq_j ^(7qxi L)il7isd,"ZcU op<Ӌ5ZyL4<Reoqm៙z-Ms@hslla*!;[% 1&>X ѫs7zBn"ֲ[V~1.Ňj(BaY9 k̄m+~<6wYQd܃ ^#QK~ Tּdp˦{QSgwD2Z掚|^T0Z`~@sT4D};KT3:+kLo4.v`h)%OJņj\#Kxo( L[Ϧ֯0(OщmP=ʡ\Ut ,h9dǹp- )Z=P)/F=HN\FQ͌p,5T\9e]3MD6xZMϔŅ~x"(&XkX8bm`NWMoe%DNM%RhJK=/ch463R舐j^jz:3PkRN~1+A#y,aפ!vT\]uy rIΌ |x?*g?\{P!96M*$2?ԖfqȊ{>gd\N|VK=z}x&h%=kYϙuOfLԮPWX^Xq0Fb&g`W.ߡN]E05߈s;L^z*SoeOV:IjgA_ _ZخrY"0EJg9Al:ytgDVY.%>khy1p| '؜P:)4voq  RG_J*ۄ+дؔyv|KiJJa#v.ʏP0h:>0%&;ԧzAWP^5jzo%q^eڠrSx=DjlcWrsE7a.4|1,bK#82G o͟bC&%Ny7VM(&L |.¯;~"ܝ<PjRxskqS7O2=/ibp3Ɍ YAI ^Ć6<}+Zd(0oUəW+1F6eqk ۢ@_̷["2`]| s~,P^7Vt#Eҩ&?i2ђHd8am@EE6j@jxTH dM<{˿u _6ʍi⳸;s(]9l*etՠYذ8$Y+ 5Xӡ&I<8}'ӤV~2CN#@M91tܙlR ֜fff#ڑ.iTxw0d:_;R{,8s?{ w^]*AWί2shrBѾ`'ȮG2A.'J Y讚ќ_ҾjH" b;g=IN!d&I4P,H-n͈l8T "f¯xH[|b"wTt5NP,oO@4gǃ 0I}$!n( {59QY ۰X;*Dw"9V!N>?; *Y@ )NE eAu{`sCCyY්)# OPen)ѽ NNྷ6[v< "aM OtZ}Ա$/FqlǏX tOZ7;Ak7-AsQQ(۱Xݒ͵oӹ}۔ }e]O0g {v(2'Uo>dMqә{4Ohx4AO$7d3 rbl *|@.H8~WsDzg6z^)ZK_=VYCl*~C 44ʬ#2k1"T~)[ ct9 -cz( Dn tB %Q`a3Z'mbNfBZI%%TX, ʅd<-0~;h,;_Q;CWd$Zrhv-h)=~*#!ȁ3:)<}`.Fa)du, [;8zP y/خRhFƾoF:UNbeT(L.p^3R7Z̡J\]k"*xac{ rUU;/Cd’eL=}%wxldAriD|䦡y7ix$ tn5ʵlʕw{BP'|H.Ef==LT iQq\#+e݋3hH/F[Cd$|UMd3@ea+nϡ9 8MV҃AM[ vXFԦgovVLNOxjwd/I)79Q)ծ{P7G)X*X g}raД;$͚iޡ_[ E뵠Bxفzt!Y)"RQU=!T끜(qqfR8GCXVԾVFk?9t@1[K5JW`BfQ<'`-7G}B_Dɗ$‰i^E,89{ɗda2P@hBO6ObL|ã\o5*J$8c\(N-Gϡ%DKfM5qjВGMZ|{U n9գXXl+T@ˌ)tyC7&<~'ɯ] NU'9pZF 4&~ip*lhG)+%&'0l'&+C}{` 4mFRB*!mАZH H&UX_dD  ρ~+?Ų5 %AV>vn a_DXˤ] qqwBƙ*iB7S%\F^YNfbXVx7޸tҜ I73 ݌?XfդbİZZ0+ H& dƄ$0g-HsgPٝdsEXv)0%^G6O6A'iAwI, ; AUUgОntya;+~םVqW:Iz%((lM-gw=?b so 7MSս, TrQ@&S#w5xVv4o'Q6Ta}J^j|zUraLۓ#rO 8KfTW|S+L/C^}[:lاݰbd|eߜ0dӉ $;L &&ELg=@,'z>L%eDsؤeypDidmâ*@#-%H~. 嬒!`(қ qnxJA஘y2iYuPuTZcE,ap#,_{D}6By.d{XC;snXuJoHOv˘ x) \˨k Xpe-\J=1cBTc-*T RU=IYQ{o##f3Ta9hc |U_%q/cIl֭6?[mc#qv[I*8ssr$<fH)xU_Gk>pġ$JܙpC6#rH%,d1[~'@qig/O8 /S"Q  wֶutnP^Ns2nr] q8^Ky~ UQx<,S_̶K䦻<ܸ2YtG75Q/oW kC۰Št!!CrNVtTPޝKLFS *=l0QgL^|B+eԈ+PSd8 k+,!ƖD$&rˑq6eī/&k& çh4k5Ȩh4]8R[bh^*ݺ.\ri/ j4Ml/MbB=iK>|6q1_ ~j1"n}LR pYtdlZB8*RME q z kslOEEَZ3]*e`΀6Hl2Ӝ7#Ǧ:-چt ZqdM70Q3wV>T}!'` td!\(ZhȓZAݺ @uݲ&B K_bLp-T{X 0UEH9"PF 0okQu깾 *J 61Iop;oJ:A?)~i$VqV os}(83˺n,~qގd\WX8Mbn4a, qdeo&>ܺ }jaPmyxlo6]( "jĝA ,N,ĕ-?=WSRvj_2d[GV$~lj}=6@[wgjs> @CC:Oӽ/āLh!s!.nOeoxB<1J{WiK |*~]☊ݚF)`j X䴔K1\=Y |+HVR$3 +&ú͔z!҄?w^C<4sY(;VĎT7Z %[Eh9D g{4K^c[ fZz7w`}LQ]EQizԽXEQ!W #+wY Q{l))${I *u,tYsrk(M/N QYZWᚾăVRK_"FT&k=Kn;ߌ+Wiw^zܲ _ fez3?AIXJP =oːeOdnKDcPt!tMW٣5=6BJUdOZKhS%h⽶vVP9mEZ= OC읻lB*/; irx[!ѮuV{f[&absV+UyڦϞ65)fX2 j\Y3FW(LsA+r]KqӬ_rS*=iOgjP<1}_;Fl(ڬC+tJL6K߼kӻ7f|GA>6m^^|r7DD#\=R]!;N3-dK;IJZ,m^՟Q&I#Sh l ]]喵c%OU8Dg|1%6OTR϶'QA`?].%pGJԷnZqX0115)wQz]"u^FVaspE y[r\v|"aB iF2(yNQ=V@U%8(i-aH L%Z{8̯v‚ =za,H=Ÿ$3h⽥EN}2wAP;oϙj8:BBP*SMV!eb76fb{a5^:י|бՃ=U.\ciJ**YٰV:~Jªt"EY^YT ЀwSdhd(wt?1u_fh;si|XhXŲjH_)w>L`#;.]3!I.Bbe׉BF&<|8lhlmTP+ Xwv('j4|߳X4e'p}AB,*ԥJBtnŘrS$4[TzaNA!y˭C\sh&ӎ +ؿsى؏;jw0oOIi,VMM$khp6>qaM$3l (h"B%fs3ٙ%0Y*ab^D֪G81K;,aP]0#uzL}C[0̩s\?NT8Yz#y“R'{}U*Nr h婍"8 mo+Y7.v P!|)0۞s z#t4y%rԒgzS̓O-_d$L#6>X-B^䇹%G|4 d )(w ;I RgԴê3ƏC9C(my@C~ si1~V8֎o]p0OtpU~>ҙ: Y4ͳ?A]lgu٠!(~/d,!0bϻ$"B &ĥy(چXD-1;7fUE^9 /{HB.Q+'+:pm|X#D<ף5(dV'փOQ6Q1qG6bk=%HJbgYOڑ6Es{F(JHd>|X }/nd7ctlυ9SNme;Yuaj=$#y`jI3vJބ dS`_Jx@=Ě @#XH/}7z`2hsC-zJQ,1O@뢪(܋kW@7:'))G";ԠG㇭z`a?JKmxn&v:JHvkJ{.hQ'{C@Zb$} -ǁh(WB dyjZ^H"h-m50ْ3n^Uޯ@(G2,rӋY:ǗV_z Dn秖v5%\b R)X4OcR<"*P@HK |}2-E̜P4:ǕDdc8n\tj>V?ZG?uq8s<㍚9;{Þ[1QJl6.?+hΗZNM=30GIQg {QQKPe7 F*KKѢ@ktQd&>76VK(fb?oudͶޡFm}j9m/m4 P;Z]{ zE _ےIl$0^Xʁnx'/ͮp5鰢CMDcPJLa`T1嬲I[NʰU6'[eV.N\8y~fvƱ;}+W?0 mzbyqڼvQ uMc^%5U+e6C{yra. "Q^JsX|6(5fEVsWg_lL!Mr!pãm+jg;X ȗnOjrI[yr#y\3Tw3X8-OE;3[#Rė&|nUA'] !Y+gd#Doֈ8$:0wlOv7T$4qۆ\3%ȥ`B~ 0 ؒջM\D,J:e0d9- Ty4`zY ֪P4_(LC7]tz|'.2w$ca(kZ6`9{I$$90~R5dLf#L 3uBJQ7JnJ_)qS<9'|vy}v\}jCUfe.]ZIf R7 Ι{u@v?Âeo#j?q'!Ջ$*!%T]ވKY㖓"י5%*=l5 tl@{T_gL)d¾y! O ~ajmgQ0fBG!"Qg߇}-gIVZ}_gD>gXBhmb]I/Y.̧SoF;#J3v~4)5O|Cz.73ߛ;EvXVW{\"'0iʫꃊP":D‹؏ֵ_4z&2io:8@'> EQ;Ӵ~c8kJ52WCEE@K_LX4MSQVc$MȲ h1YF8ʶX=[֪ŷx=!`5eŐ{{IdCe>{=nUE01iAAj4p0>U;(q"fcmb.3r4H ћA fXNV2|pVE惦n$,'(|[- ),;Fƣ(5Հ&0Pmpv6 -Sg 8V26^HIU5@```02 ܏ t9z)P,3VZ85hPMJaB[AOl&f~YV,k6b/5U<̝Xj,B{bIW&* xԴ",n$65lK/9IJPMf&rC)i1xgqlTmm2ީ;Zҍm[+m۶mYm۶mmg} sMU=x5.Bay6M4=qjdr١__ 3$rȶ(\CALz3$7nr-Lvy\\8TW[a:̮(%Ɓb!ď{~'6ÓOps _d(F [C5]a.yuk=gTs~x=KeR}KD=_G6Eq#zCR l_/de)ohVs er7T {@`}* !!s˰ ݙ9"Stjuq!k~n/kzmK?kn6I!+gh!.My6/_6Qssbܱ<N)%fhr+Ԩ?[u˛\{}_*5S̬y}v]2WQYضq{I8x]I)kmMCt33!QZjVR_i&'UrhN.ucoĮϒDZp5CBR74:-\0oP&1J.y9l6I~}^cO+$N섮# W"D\4dReƇ^%g^D5(jQ-q a0էY(j'_ q{r4kUCy *˂h8h~(qfcV ;q zEMIэB$t2}9_{vӿ?+zNa, N>=Q:e̯WXQ^58PBW3ǐ1K}\ 3De02wh ZB7 JZfaF-(XVA@tMҀJժ̩D^Ѕ P1 b04RZ)F =~TZ&\sEreSǍBp3M6ȐJF:f>M3+kcS 4Б# 551 ¯yknt–TSңˆ;{&G@R,*:E &F ꭐV޼KV?mx35ߚ>chjUj+ [ư:A5+h:wٮC͓ |O"Yf^ERɾ["qT@qʗȬ-g1։3+V;Ԁ[P5Mώ򲅒]W3~݉YJMfͅٳmxgU$/RIŐPA޹r= 9\“5$Aͱ!=}\>Z QCV+  3z;Ȍ,=s=>)]>S^Vr̋E_ai6HlޥIխ~SjT8|%EkyQpᎮLb'<6j(ӕvrv *M ^pcX ,eF!ZlaOO:950:j̵-Ќ5jṶ\YEH?9 2jW04N `<vCZʺÔ-{Aփ-8(zdj_@?KG,s|'90JL&ټ"ua;M {V*%x{o_yxñ$G&Tʩs{DmlḘ %tdXZCNb5jUa)V^8 ͹MS&X" È\,%Zvt w^xvmO`l)Z|+uJu?,.چ=OZ dHS֣@o%%9pe+(}}ĹI *]vj|<10HW?ҁ=/FqV"{@0SW)nd$,>u4˖MŮs3D@B0g+ڏ/$Ei;R LS%• O?nl^Ym0"}*4SX 1eeʅ-#BnǯE`R1hݞ”,:«V EG?R}E?6v ޱs徥qd5c{\׽meۓ(|L` 0j]n6CO.(DJ(R O$p1lx_{LWQt`i؟3QIsG{+(%fR=?G'? qc\& QL'UJ5{&Srm.T9bRb;l8ahUa#ΙpjJ[BIvT#p]Ǯ?%]N]B-hEEbJ{Zހډ,:,WgZ1-iof?$̙(0-HrS* E͐Uf!c(g#80twx#ѠوU{O`Hh: ԝz+ϋD=? msAt,CɡjhM\0ch޳_XպL-IX1\>5X{J$p0Ahm2Fhw5?sɍjK"-MڭHxPn59P읇aҖVoBgmn{sJ/dhţRK6Fϩ!{̣pY:?;.5ohH'zؼј>x$$ɐVi%c+VΓNJK74Q-6V/Sj 1UmIufx8sQ˼bb^%3|"|yyQUW'j7hEn-@ᢜusNcIŧ3 a䕘IRU``#k?ICNH&/Q >ge.ߥ&IN\;Z y?sSYm^8<nҦ!Zޅ-4o&Dk٧0+ot+GUv; HmJ~G#۽MidbHRp)D-h1IA/\/r&Yf5.xuVlZP+%3vE_`4JNbpo7YA% AչdgIΕ1tg 7v4 ~WC /srejɿ>k*p 4Z Z~kT5o3Pͤ|i@t 8m ?zN8򋓋oNFiҁM.IMA6\<OtcCkoFmzȘogDfۼ9]~" 0G-gwı1#A_2JuY VօQBϛ7sj!:xu:*(;%gC!%."fqkzL |2)z$=kRv==MlvadPL?w?!Eo6S!{ FOK`ȹQ|C>G2@=ҖC ڛ-JC"Reo6$h̀LRnӽDM6 _<'9`CʗebN[٩_'Z"i Oyi]A F' @)"TAhKy kohOJ=y-z~c8K@*#F9i32޽0 Ω1Yۖ;!FVcoW wdAt%1uFK.R kpeFT#=m 1W$aƭ5}M`cagpƷղ4ہgGsEw)-lkj@E:St˗hf0/s(&bb~z}Xh}T=eEVT C!D$9|o2vcC-[x Ե؇.Dx|X1G_*3y T¯ iTIIjGy1?W-h7Cޘ?u dmA~|o$%T{/-:DNHȺ\Kos:Xʓ:lsCإi7Z<$!:ɧL;~zyi8{sЪ]8H'eһV V$@ .fd| ;K2)-BG,EbE7*DE2s i%J0tN12oR01׌Wo*%dpD:Nur 0A=Rv)M.N: v(_Q!Q_+tYAhIF.FzdOjB6+)zv#^r^ iL4Y$] VRܦ ]ۻO|rq}eY֗;Pϸ",`Mo}^@j*n*ρYS ՒmM5P/jILg,ne/1(lDcVBbީ mҡI'<[c(꼕ĭ𪃖u\JLPPޱex8Ѥ<#W?[,7qOyyogRvÒ -f(\;cGHT%V7Drd܉SXXx#Zp3%'D2IڟkMx :e(ǀi&BG}rS8h,"'f YoA{))5Ehtop{L܆:jIӘN3FdN;^piVהMdz-x긗B/!y Dw*C46No0&GPɵbK_J:aWr>1  kjy;k3:i !2%% b QTeK$C+u y0ZwÍKJӽySA,4Nlim⭤B )Gxޒ1che_In\Pnr*;F0 kEJ!WɣM];"LBٕD7Kl}-i}lv rO4e?逸7Ah]JqwfgieekvT'+IWg:qKd:Z蜯wI-)@{'hEf%\EVe|h<%!Xۇ7cfwP"n~aZ)R(':%wȯ{Ҽ"'d ,k0 ,8-Xfgl'V_ăC A"qY/ٖOG:mѿLJaC:]^X̄jw@oHCa]I) AK :P4j"k109 ſG^Ii@̊m_.p>Յ0^ RD&h&@'ίMZOGt[zHȋI%ծ-ΰ-}.X[6o,ܸ Է#F(?A6 hTOXcuSŌJHՌD{Qs;.0 go(9o7}ᚯڈ J'/!g mMetTΊ{qI|m7#_0jcWTڮ- tnsܸD40*^t+9~ 'n$k&ŐҀ}n7\#uu1Fhʔb  H2 9lҡ-~^ \PT v]r#҄ SZoCI9KLO{րׯ5yyuPeD6FLe/Q'4.~ \,\gP\evk F67eO(ϸjy%B4bAR WG4>wC?GLqd40\n~qfoݟ~L; xV#qqs"Xz0-x$ъY;37z5 à|/ix8rŲ6cSƷK[+E' X|f%% +Wd}`-WDF Bl~= &,iE)l' Nk^p֧R3[ExrfuonF`1Zx|WA`dKbXd*g2>vrvP>bqa"&EsHG뱃,*KDzԀnQh{hm@"ZӚ+GTM{SU.T ̆s׏$e(vq?CI^^YN-AƼz_zEygz jaBj)FQ9,6eK-*q_7 )uFIDpGk?` Uy,EȶL0oPX^42#;:.h|O:^"NYx/*[Hi6C,/~᱓I&k\`j$ݟ͝$L6>,y}[/;FgZjJK04PZ#zҔpB6OqT>9)e!uXZS[( 8MGW0 w@tPA{-  oPRQԚ)JsA}ǵ2h[A4@_U;a?c6xo)0e"??Rb^/EV2>'>"5B!G-G{2{԰.?&S9*'Q=`;+?z}͟^f 1ѥrt"X[;>8q|)1@],UZ*]Yb4 iLbSD,h~ 7WԤrwͨ.EcF_il?t9kjo  [Fhѳ(,?l>COyEh>R&V,yt Gu执K߲}ł6םuhC 3d X>(Ɩw%a1!UXT-P8B1HI3k>{UHBhrYˋ^DX|}ޒD߫ăSWqGPx/~$4)anY( 4ۉ(FFش=Ǟ3fòXd.(x')D/~4EZY kORuD f}Hiuh}oCNqⶼ,[Dx 1nK  2&3oz45^\get˗66V[ e:*jTIo' <6=> [7{czi'囪c* ~(lafu}{їL ,7O6#u( bB† hkQ+tUwS4@n_nBػ]Q>l3y6CygloW2Cs\FEk!p'0W}^Gn 7j(R|;\ܩʁI$ 4UO+Vѳbߙ w5LrSrB -UӉekJQC{!AJy,GB?]{.HƋ*{5h[{GV89@ʻ܌2_Lq$ M}}"VlhIR g.!Vf#Of*85y-\r\ JpMJ᮳PmغW6Ll*}ykxʲ`>Y7&cS|HŁl-A:p*=s@ƌY9Scf~M &J>}J! ]1*sD&E KA<蒩zH<}f~+NSCLx\I zjlTܞZ-.aa~b|4([Ͼ|Qig/]_V HQeofO!/,dìHƅd^1F;H7՘<x 7׎7XGQ)<1Cs+hWk|?%3Z2l׺_:eW+Ǽ1/h[q#rfRqJLo bcqy4wLS UbDyXxC7tq5>$T|FfP_-2͡߿bn6hlͺ2"@M pYF*!8ZGťԆo(2Qax' xꩻD&'S,ɧɼVV!GuN)%TƍYf;h@ѧvja?v`mkXDX ]bɥ pUT1BIv$MeO/;+]&`8H ζv%|zyj;Qq^GD#xmPŐK%ŪKξ8l7%ri^W7 P}]3ߊ\[\e"l;6N,Z7S9]H[۳Ö(]@ᖌ Āe8RR:LʃIy8_-Ԥr'@ { D-r % .ޒJ{T'JJmJboc>?wfΠb/mM(8_*L>F yKSb},w1o=8;Qlfx {VП(/t$!b%h\r? ˼Vh%Z1Lx>~  1uƵ.f=ȳf4M|Θ|5`#_Cᮃ ߡI;35I=sc5 7/Ku'.^8RENƪ~_=z]KZP299 U-3S'w㖡eXz$RA(C] QV  ]tg.).BKyFp{0hd*/5VrM0"x"1xv%n?[~sP`]f'6>Pŀ3Sⷃ<8_`yq9fc3t2)AZd!֛YdE]ἕrzF7 a2 5"TLQd-$:ov|.4bgw®}3gQNYߎDQqm9N**G3)"n݌/5r?ho $rK` xi>gVL%_UM-%P+bXzshۘ~bΖ)"Z6'B烈ȟ( wXk؏W$s$`L!#+O4~ 4J5^ZR JQKaߗT7 ڨ7h4 xC,S4j;Xc@W yX G`*ʄn.cWs;ZK-RbˡRžj`z9XRR<+"CͦkU^{:ߜVGJM%ht{S9f#u.>ݪxɹv c22y"x 'ԈrwYִ(a{uZdW4H t%~sMW.TФNXo̩Ot0xYa~b~(7we%œ4?o֜*ƛM 2X=Ƞ4P,4S>p}yVB;)˭DaOlxBY(})$64/w%}=}B֨}7y}{קXB}" bk^1ŧZp?׈`2aIMpQwvhm5{26T!"J+jY9ͯJlL칵l?CrpщeqO {jzzdWx>gaz;ibvQpK*Q\\,"$Zm_]9Oz TZ0R^>- K{'Aps: o!T܇0E.%ʹ4H ag_#[ Ne8x#RmD yfk4i]S{%Ҭc۶m۶mgضm6&bO2q21-dj?ܢʇf_uJn(8A\wnC˜u~zy%k`e.kRaLrB+V5Jp1Yy\VB5:\<wqg)mǾYE(Vֵ]G|aDx1!LKKUm\ƹW}q&Jd5܂EwOR` N4äx(–כ&y* 'qޟ9K eS6 W#E&4Ȑ|'կ |'oEcuh;:?oѾOo'LK ~ Kov@Eo(y!p/Ѱ:ݺ-tm9UIX֗=d-Q.;ֺ8t-`R: īaGZ,DNS2s~/݈#z1SA/H 5@7/:]ŀ_>֜%+SȖK +oOrJbl\I\ݽzxG,v;8B,alY} 4{qˏm5nW֦?62EO$kK ̠- 'P<=^hѠ|)zGR+b30Y=R+ݳ"Ƙ52i<0~`B`8dz| h$bP^N ŇGCq! [צ88 Na@bWpΣ"sIK+&/kR@j i9-9{iSXHn98p 9LKM S.Sx9i2Q/!uU(Z.2G*"] GpB,*`OƮ˂ҬתdE[~?>8"pBnm:9 g,Y{ocb,[8b5늂]PΒ8N7P=LtFV]ܯ"nC8|h3_cf}$&꛱yזT.>eQ`?v b!.-IjiEH4}_-/v~e)vKE˲ 002aٖS!'/S^2JHfvάuDƧq pπul3[~o‹Ո7al]뎼HPk2k5)S**k!YyCJu>RܿJjU۷nD rOL dm@\{ėKa?~[e8 MbOqy~gٕPn&Ӭ|o4`Eܫk T?([2J΂F&w \CoZɹ\-.LnO5}4`|J@N6> l.Mɤ.a)̑P;aNI+:[2Bӓ lALŰ#dkfqg,wW E)ia/ulĿTsӼYǏ7sɝYm s=Xxտ0FBeQ9^xk%)HME`I\ 2[C[$ SG j&qi(Z߉?w"sQO"$&ۖV*ǻw+. S_ Ck4,^QftjwwW]Ve| g HP͵l1ix\Vw]ecqbr3Nұv98BI l<˜-+NwAIRmp@!+@dM MaUf U(n_{- M^8D=xw+7nbg&|9I:EY@L_ 1qcUhoה ՝6@!TDZ Q3d 9zK#?0JWqBo:wn77,H n]J"q. 0tso}*RA( \},7}6$2Y%Dh:|{GH_*CrK\@ޙ3ES":Œ˗ZxFE(^$"O4}5 WlG%."5>̐;P\=ЧuK}>$ʘ!:>(X$n6$v( V!HBԞUJ=C/e1aƧj@KO(m׷caHoN<9 +}ܧdv8Zd֝Ӳxo00o( `V]B i:\N04ϢYLj9eVG`/iQi Lu7$.+gi20{ں˾SkFgG&Z7dqemr6rUJU$Bx,C Vpj_ņg2ȕs+GI-b[@r Avk+C2qGənFskJ:R}O`e6Sz1˃yBЌ_eөlપ|rDGG'T% ѾcS"Ul1r_P=Ab"CWgu!6A_9 6tႅu ֊.ϔOV󳑲~7mҬa@z[lLqݍ̪NhT44>7qy4z.A᝕ P$dML^ vBQwG}zzW\^2,! 褗 Aen_JC>Gz !'{HyuTW]?TOYari=~)Q>M+f:dời[g 8q%瘴dZjaƞ6Ft+Rđ{L/WZdtb!,LxuhbTvtA*œY2#Egs^3`(#$4yYj %/#ҡ! 1qb __i^JGsi&;b:<"=C4/E ơRK;> z<M_F l!cSkR(yH4vdG( cA+uxFc:7F(Z3\]7vGN[0iI3Ȅ d(?O2Tr(N AMP x2ErA VU4=jDFV~`|^sNX㱋9azٔu貥M#gd0n:,m~0)/5}ݡ,޻ʡHm %OM.cw߿Q7۬I|R h݋CrIo!q$ܨeCѠz>YKMs#;IR Zlw9 Qz+/u3_  qL{9R_.X:)2-OX1f=!1Gc;ɤ)uxIRP|cN𭢴v{([TTv(umt2.K] ҬĒ:ŏ=%#:bMNGT7ZA).`hr n',,n=45W] FVM}eAEt/I;+o{`ŏ3?++B]ĭ)eU ֓s- G^MzB~} pI ;)btojYMz/MSƬ*pVÀڧ0l"D^2~hbݭ&s&)he`E'F\ߕDc"x72(ğI ]? =n Out&GvжX-@vf>#h( MZb~<Rjs̬/W_-~г Df 8hCkIlDyϕQ@o!#QGYq  VHģ"5A6=VGyik!9tsCmxrL]1]k"Ձj'W8kh$35T(R?M'sE,J6hw`HΘ+`(K[p]?b,?rΥ \TnJh 4Kr>s\4,b\\ZXU( `?0VlQ_ s}:'+`u;byT@FW" 1օKBd|w4܆֋ v$o|1ۭ dSvuM1UpUz&UuL}.FWֲ&*R  /'C>5ńCh@뒁1C%`njhvNGu(*WRwNQB)wi8J4:ѴVA<^?~6gK`^ܸܙqhbBt͆,t@< UNGVLVwƬp5tTܬRN] ~LƨJ?)Q/yYo}$V<"ʄ"Ao%hD)JeqR_yXYfA2HꓔCԐ  Qɜ=֯Âaȇ_.a\pG19qNzfLj$Cris?,.gxZv؇Xιw`])_'4j?o mh_GzϷ79kk=/5 $seL(Y@7cpWTߑ1z g(q>h1=/קANOn!c}_p{ [ ӂz.soEq;I* i'HϗIn,Bky\}:uarKBԑd6]e 55NK ű$TP_* '3bBCj/ f<@)b`Q 7@\6/q_ƺ9Vϻh+m3?b@K= ti{4'X?CJ(;){k8[6P`@Efjԭė\U2>T\[!aF*jG&d-&1W YWwK񸰒XT{'T<ò~x -}VY6*"5# -M?`|@fM!9l 2W QSmo'dco?w{grzTހVȤC | eWVK͔=T1ȐzAI1p 7~xFۍ,ov@Q UdWq ]B5r6b.jo!k3J|HAv![K`t\o QQb0Db@3/~dl<3L^ף71@+]9kzQ=f\V}^am#'i.'cSlr)EulliVz$SЯՙ[gGiLaҢqQJzo55E[ 7d+3`H"0d6 \~w>4 sHO 8fYK/,6"d$ qwJteXvwD3׸7CWlRA07si‹elnOY2VR6'ې3O~ GuۄEXC֔ a;xA>ҋ,-_՚\_DjuC=fRֹ48fѹȼ/B$z#?Z+!h͆0 Z[3x頻v~IO R.Tu啑>EB| mIa9O>q2s uΠZm}J]X&qݯ&"{I <$1پH;šؿ" * uv#!Bˡ"4*9:rCEN37E_FQZ174[2Ke=Ub!Poۻz=ȿus☷GX:j-:Z>~1hʢmjhk Qpg0]Z9.*$=Lf0 =ΈyN3ơ+1Y{a8n|`U*KthƩ` "YAÉEOb6s_7#/ AE<(#kV%ݰ+uwLSL9W/o0NV,LS!i-+d^L)( PB ҄ot o?sFft(< ^Huy6+ !&x |4~U y5^g!v鼽`T@_jz(Lk,_8՚)L /ȢS2`Gh-X|'SaBYMG8jை$ڤm:%k'S* KN5]DžR:6TTpFr9# .Vׇ*N7Q~aΊ0am63h9p\^^vi DL׎ O#T:`Da?iC'}vʤl^s9I3dȄL6MuOzut! 0szF)Yuv! =mob8',B#c+x45@|RjoKڗwmr`LF!+YlMY (ad18-O2NIPa\Mkb{47RW7`jMq߼@W_'29%7q E1 uL~Qqv"⽙kdJwe!Z)VFEO#2 )6?hnL@;i}|ƛK@9OkX8w?Qn'u _8 I~5̀uVֺ<{[U5mdY!һq w%n#8b x˜57Q_F385](ԁk#c(2_ ||&T(N!9!ɺD:uOTD`p:G`ʝBhhME*;{SP r" 76FAEA3;%ʶ\0~T Ѫ,?kO(pB!vi@Bxڟ_+g}}w9wĿK7jդPkլuξdhTG׀TA͛ĕ~x?n\+;%@2Տ,ʖ@*NYxNE2C'DNn )jAw==ǃygpC\4%MƇ`08 .fnMʒ| 78;gPv0; AĄIie (SWKsOs]fj@iNL#H nk\w14pk(-'( 72_ׁ}]ʘ[P>UsoX9`P7')t~n0%ku*ҬrM`%Qر,CZaFkz O?<&}:Eݠ>R샒TјYd֋ PsDO509Bdח5xw${haEvCPUR}g$8)[)9yu%MxC)vĿ i#_,(D1 b`|G?(UԳ6+ Zx-o-B BD\Jɵ 'A钘|ٰJM]1aࣆ 9x4s;͋,OԬ+j*ۉ21VXmāca'Hsp҈`iB]uaZ~b M-mNQ5N)y=>y31#{g`ne4yGuDCO { T:+R*2B.NS-$ I>ŦYPSkNЅ7qc EC N<WG sw)'@q3]c'/aNXS';g{l2oYP#b t翦exZHh,<)sMK)}uBBFΎl鹹cCjWCnW^H:!{d;bsuU0L=&q swD4;9ߘRy<+W/^pưktv3Ik B@qw]p(hu|吹L P=bڲ,V-71d*gxX膡t$^h)UMH2#|Kɖ'Ck~ țPiZfv]M̞$&!::ˎS`tb=)P|&z\>0؞m=ɦŽq=Y V.K8::uQU!zv0#5 m:V+ }U;eeoztTưWic0wY{lP*$I䊗l];I%+J\wu 7z;_ ƭ'"mج +=En3(QVm{e)GD$ m\Q">YđX 4AlEs?0|y#U&HT"BOr8[[xi~]KYF_dZCtI#`OZPԋ9:"NX"w0!er d[s3NP8q+$ig}үƩ j#E6X7{aa@0?#lyR8kMrlL!X-EoűsFoPAD\hG&#?$jqAhnsNZl1 9>PIr&)YXWhyr0-qA U]GWȁ2 \S"Ŏ/>487M[!iܤTWɫ?ME! \ѕVX9Oܯ"0+g9ttqS} š(gj#N?7@ycVs˛!@5w1Э֨j:_croLxU&;Sa4u OBrfZC敮emn>a~%bq"X=E(KT(];{K5ꪁkzqҨ@8ٵZdMLjM[Ϳc x~d>i lܑS[c06JY0O0~<uʰHKVѷiO"˙rPeB_>>/UفEvHȡ~ xr{;1$ɰ@r~ -6bL@ ~h_kݨ`2-)SU^4 O.6z?[_$@"mH,˃}%I}-ygF$;Msu j_ qF "G:ln{N9@ ȝD%~'>pk[\,?@r'\!奻zrAi]3KOF͗-I;C1߉ kaԣ?ϭ?/c¹+r.(ֶjI*@z"`UK(qP"11ofźe#$|{-5Mb+^h< eYA6Fu.(PtЋlqXjJov fo2(BKbr %9bQK}D鬸oi1<Լ ;a0QB3w<cѲ7ͺat8RQ"ՠ=`Brў'?N뷇5*5+ȮaMajs/C!Wvݧ 6]5:g@=!!u HqڝKk& 3<{n̽ڸ-(B)W +Ȓ}i2E_3faYco9o߬-24 ߪ.  ˑC._BoډN!{*|sl9x n:(K; "Ƥ'dC.$ doYQ R~d5j .BVR{6 xo:m+BTf S4,{T M;dN au*DjoĹ*l[7Z&U0,,ktH}ₔ{kdW=FGd9!eZ2Fc6[p 7ì~ z4 endstream endobj 161 0 obj << /Length1 785 /Length2 41144 /Length3 0 /Length 41555 /Filter /FlateDecode >> stream x|spo-Ύ>qvضc۶m۶m۶m[{Ω߽ժ5{tc"%S77va(8)Y;(8Yiaf}#3keY蔔eT 6q6w0P`oghcm4G3#*zG{=C#+={ 1G(98Y9ĭ lmm fGS#ٿ,E[l l͌hW@ Z2kR::rѹ Gk`cEbh P0r0w/aV?M#gefo/P33W#C93GSzf&F -ͬlu4 S2536rp0'ddm_mhfm/ a9=3kG%7m3[Zkph3):X#+ @`ga 퍬 fbdjdlchX3<)\Pyݙ}er" SXXG"2gwI4} B50eǹJBl^N7S8o[ȡsHz](zjN<^.S]__ld"(Ϋm#Eޅ~Ln62HS0O7]W=2<;ݐ_ÙضU Uک>7g̨ t5cܻH7L `\WY HzE嫜!)sv>ӯ l8Ow1vwLOwδ 繾/'j/.{*7&mR]YnٍMK)E 3AVC"eeS ẁ7 !sbDd[TpLL0N[☋W|!BwKH~]C>z*̲$u}3RZ׫`H:|(Ų`P?+xH_!|`$+v+Zr6m_ANL^#:h}$q3VA k%$U!c[ &incOpÃ!'2"W6D>k^CJƭNj zvqa"(e.iX5id {Gzr 3#2(7d+{FO'~3K:m L5S.vQ6m]j\̆JYNڏd[YDZ;"@pmޣPмk L~˔̦aέB>3//0Im*zvƣ`"PbG]ӉԢׯ!ْNgV_>"71_6Pq(4z!i\–[2kdC8][YNͼNS ]U4b~WB`gY{y5tqwRvprkOïlh`ψyȕ+V&O.H)1wzg|^ 2 N1ZT1#u4 \܌ .{`&Г޿(L~I}w?N0u)(9nCb4v#yWV0(43 1d'yMS懊m[rvyrwSzWYE /A]-h]mX =LҦ09|Qb4)ͱq7nY4l!_<=MDL|f7 F^)ґzijN;s oK~Y`mE9h.}cgeٟ;v~23ɀRs4Um Z)AG &A8̓ _9`|T=X1 >`1})@KL.T[ رՔ 9f "eȀK)hzdr‰L87yr̓ Z0iKD|qFuY ) ew\.SA tX<϶B0>9"utVᚣ|U߅t){Xl& |-6;="O.Uܰ&y-1[s 8m@[^)DCY*p0fA+ASzmEԢU+FǛ7LѸ_"r r'Ъ` 0љ]5Ш_֬oa=wef0$mҠW*+W:۶nxoCe؀|Le>mUꞶ`xHSNek bΕɴ֣܍8S$k|lqj 'vjdw7ZS4b7nRd"4<8%=vr'|B*Zt{wEI*{m&z`C̤/X~g#nO.7aǽpA^gU狈5tM7䪧\#ņw<.T}j;pa5Ql+#t?I t()J u]?dt=1nB5Oz:>DkV`Ju9iQq&cػ"ƕlUuPZaԂ1au9<ު4̵d H= R7ۜsoːv&xkq+3F./}!~)B"=*%%ZϭFbk><`W]L Nwn 6P,2Csл(96䄭%OՁjɠxqF%+]/XM!дގO8!~'dg;l(VK"X~NC>6;?.`a֑y[F"J>N;Bj\lo8? Ap͝lӗXuX8KLE;h^LZЩr`Kzɞᩨ]v+kI#XO; T>.Co҃/бAexv?j`i1єkbdҋP[kʶ_uv72 xcί}h08Q{־s *dm6׶( b #*nh5BS HMY:(~Mז_t>7`%!>,QӓMNꪾ ,E7Im%"C,TjŒ\]ԯl <樺|Y4.2U$ QxS"SWD %/=yɦ"ѦRJVuF50fw젳.S(8}/~688?PnhBÿs2p4HIf|r+lʟ<;|dcd-2r jk924 *d~\0 9>)U+`T7>l)ԇy*7s؏: $kwCfaj5I~ٔ0y~p? ?LX\uLBn7cj6.P+ }C(Ј4 tR "LBbQDuбW!Z7zW;>tIU:.iŻ ?@wd1.jnu9K; eю}2[Cr&45V X7DOix i2b41'<%Ks1?釨`Bɷ,  E޹HX8-PVoϾDOZMh0H[G@ Ń I^/@oA'R,yZԦ¾˂>U]e6%^X:l06_Jks":Spsbzkxh6bm꾇AHAxY܀ hh0XY>ɀd]Pp~C;W'9s{l1u{qR񔾛޺kmi;ViR[ÐHN%B* <)OssDh"%}@KUeh\r2#Ӫd0N)kv/II_9tH ? ԬXĠcÒ%Uͭ,Ύ$Fgy#cP(RjJ"f$6 *Xғ:#@Ak@d;`y`Xޡk'B{`Ӯ`a/Ǝdz3Pb2+$ 3b^W`QDo:@ӌDkhRS[+f$d 9u~^H4˴,+HjfhA=F#ڋ*)?KD>QֲK/vpQqTΈV[ƺLD¶膾*Y ͯOl#[ޓjJ$\ݢd6ZYj'`~Cgp(>CjZ!"Y+4nuizƓA%LFGB.c.&k^(:lN)KF6bl N@S#yEFq]w`x|jZe֑V%C$djmIU j-;uM܅>x Rz2^})Of֕KKsDG S*XU4#Fp7HEq.*n?D9F.M6ֲs6Ƌ2w骞rT$sv()m=C!T\o1VKOr m̃!LDV#y!›gW@w|#p6 =xؘ'XpX^X*n曃- av~UCe%қI| B4$WUi/&,+?8Z;#[M&U2 8f"?3p һz^  Je@/;'Ԝg\|':{6rEE$~:dd`K%0Q$sbSp2àgV'>{m¾)D`hsb 4h] F(6"C9VX2(tpvRN!i/<9}gg~=~ wmE+@n{[=U MQ$RHCɴ;IѶDҗz_Êtc:.ҽ=נg=02_ChD8/TysU,  U(dWo^zpJ4ڶFMQ9͈d;(ۚHx!w ? +k⁦cyH ɮY~jPqD\\ף#cH {a@nN@@i!IͲ\XpEVLe!I˨'2[WwLW^ñG)Ib'akV^ڪ3e ;Nd1?*IR ~@쌳lxɧ<>҄ cO{拵F=>UdP|;z4ْA 4aaCF'HmZx^pR79nI9: *GԭYXѦх֧OxBM:qJ@5P%m{ ]-]t&/%>1ΦQ;s4(ËEy2~6ڵgCPIn?uy \Gq9Ecpbs.6K[\=n![YsKeq%cE) kߩV9Θ ոR0ߏh1vcV2N?wX4z gtܓhʲbp™. +g35vMo4}uO  {aFaqCxE.Fb]tFuB{„¹1:\* 7ee9~hjpRc3B߾9Wul+RDk'FJ[Y:Zڶ(fP87oqaL'jэf5O{d241٫eV"O?'f<ֈyޤXe:M0v(ku9xrĒA (q[%KX:_0g"ktj0;bз@ْsnļ϶NͯoBɪQlP@Fq5UkmɮQm5~RGOyY5[ߤ9nM`gw P :oIИ8ӄU_ (/GM5pF@C~̪z^cڟqEp}D?ApOէ1jyl_\d@aW\9'}HhX֖tr0:w$82. WiPe3:{{4AH)#V/,CE@p,i:Df(Qe͟VED׉= U55‘) 5/g7y=.LnezZiJRە>DD{֬u+Y>~j[ʥYCn>mՈ L1ֻ'TGVWUG]؝8}L+6֤$9I~? r!&ء"&"ZbϦ /[|o.`:/9/

HYFP0x؞umQNSRͳ3n -$4|^Yߖn`X'cu Jlre',ݡ )_l'({̖*G[ % I$e8@TJ;$Znܺjap;س"˵7jTr3h sfYB|01*cb)OpnLWH'VX'S!g|<]ÚtJ=OBrB*fDc豟dkg;&:7\xmU瀀mfi!3Kw-WZBxo!S˸)kJ&3d }u.S%rְ21pvöz_;E)8y}F׀52I*"YC$NOOAS^uN$u+)FlSqr3ry)\7Xb"uYFY77KPⵃm+C}4=]k ʬGp x$LEq<>ljJ' ,]%N~cW*RP`&FLA%jlN}9qȝS;-eI^ LFYV-W4DKRp{v&.u&bg| ^ϕ~wLyjU49A@r\{1چ@TC=䠋N;ߝף{W csֺV ĪހpPF k y 2yh{HbZÆq"5]޽c}O"ִ—` [/ q`/+BMt{3<{jSQhM܊rMj`a> Bjk{n0jZc[o1*Nӌ;uyu ="3y7+MDuQX=MeoG:W|9g!ɂ]XP~uc)Ց{x>'HȐ I۞ ;n#vrlm5kf~D\ٟoW7vGxi+szNUsJ,5r &AMQ#-&@%$HD1-,.݀@mUX[Ƴz:;#W#8/pу%:](<(V}P0Lfb'O"J폩CN@cA̧IyIv Cvl캭l 2ldd23tED,=f i )? #yNkk( w|ѣ*&v.?l@ac#; 31ZP߼X-W ^>Uf滆m2ny[CiF'FûjFWS߫OgWu06He'|x$tŐ[O+`i6 $ԤclLHґ:>t_~^}/!+0-DxߗK{W, tkORZ'G+-dxz7Z QX㖗H-rpPH!6_h1a HlzVPǵz#?)YkEzzB-tU`t:5RaLHi "q?:-%mwer! [Y%~ ݧP ߣe8X)P1<<B -vޗQ>_x(e9 / :)xAr1‚(U9Q`Բ"mH+8b`dStD+s_Fo;m1:%ʹ1x%u]ѾyTdI]|, ԧAu9NnN}e?44.lG=pĹ̓TƄpKJ-G.ƍ+L5Jiכ=2.j2v7&!,! zr6jam%)SoE#.V w^pG*$׬ᢆz'V+;szӘ WaZE6Va.zU}dI=T9kPOk"GćdE ywt%x$ټ=sGmLrZs ׇF˖ޡ)B[ߔ޵76.)M 4(SkɥTB(~S/d7FW:>y NOӴX$Dud\6n?'N cS;k̊FØ?RHQRs4@Q ?Aa/*[y IKpt3 [8dDUhve!>*NQG} fܕ@AܝnAx _%rh=ş 1,FV۵RL0G+4d|7vl֘qkAOpbiP!qVP4G&7ßd7:5Z v1>+=b; R9ex+??{zvo|2F鱲H⋷}b5vYt/=JꑀjĜF7oKO4qF4uV-;i9ϺwCQfC _8@/N97my-v{O{, (.I՗~<H+3q*qC*{5wzx}ңB2zKDy>nc')7b$\{sl|jp^^ٻBvPUu݈E_0CLptvLhwD9.pUޟƵk ӄW^>9lC԰&6ۼ9,춴w<_ڃ7EDͦotHʑ՚g-21ޤ`<7v >jQ]Z5Y {, DF#P6_NB[]&NrnM3zRvA3 ae?^Fddn[mNcJ;tІҵC܃iLD~ +;Ț5Jt=`2V x4GHcFˊ5?۴|3{[@PVG^J3ƃvU-(-X[ag@JP(1`0󫛸rx/ 6Bݜu4/1g0 ɠZaCt<%­AcG{DZP ڟϤ'=' mtHMU8;EJpHP4D^ rg͔6tiU-`dat(`|1k\E &WJxmR(0 qF;/Af?x9`rQJ`qt[uN^`Ox V32Y  5pJo_nbl|*ۖIcr'»&Uآ.VE 58kɚyN 2Р5}bA!QɇA'ӑ]T/^yu#O$aL|"[la|XeU.G&cX1x5[lS8i@n;Y$G| V[9eZaPɢ:LxU"ƣYw,ߑ&!"Kqir7]5qGʳ]X6YڨQd4QdZ }ey^ԛQJlWD8*&/pG l9+޽vHd3;]&iefo\d3fZ+T.+9TtnYDy.eՃ[3fG~wc97&~REYʹeJw9@ xoxnAs.v{!-+%$ |`ŊjNN!8"s{(76wxq#;B)4( am"ȜQWU%i:!3Uπ!<$41PJz :W7 q7/L' n,;A brEsLea7ؿ۬xH{kO3jq0sXձBTa#wc4j7}!E$\\yFe6ffsb%5LLbWnj#PHɱmJocz5ǝ"{,7 pŻsDxBx׃^W߄K_HB(*~K< pS+%he&PA]Y- k7yAuf543j|x2(J" ,53pΤtA<%Py i! Ьk@ꏇ7ZhT2t4~AYo{BPt?79"ni)xrD;ڟ,^..^ }tos-9Ś0)1I)ePe}2 |Vj65tp`1 Da"6{gԘmO8scO#7 Jzn;.Z+w6Ţh2L}&G,b;:Bp0q8kKy"ﱍx\.N"yۯFhE1`^ĭeKc23wpS!nkoT%=)ž> j5쿍IaN\Q&۹q>}〃Il/4AK^_ &̗/v*pyNPH'AV,f3h{6:֙mK:*Ϋq$<\~8C&(Пe;m!y&V ukc1`O3  ԭe/Tin[՞%InvWiPa=j$w*Mg& w䫕U6Nloϲ"xl`ض,k;psjqRjr[6̛!3 TOV~יDciy;&FqrhjhJ\ 8fcAmN:B vDPO6J㴖 VܶBX EysfTpd`u {q>֤{ՌPf*DJȸ>|80BFUQ?ݑD£WZ-dK4J4"?Ȭޤ3St!xb8'cm|Jȫ3B4-dz-rQi72/7*;)R;UK@Wa;Wrzb~C~mTE)N%.#y3ݷqfe'%壟pD[LJ$w\*Z;5+JZ3Sۧ-xf{-p_^>IsE8Oiqt!&Wݖu[2EWdof  ffIGAi2> ro|Džb/[TpZ0:(eiGݖvq):|UIv]%?-P|>|oyT<7I<7#XҾȎTU5!!{錧0>-U$%|߭Qmc@)yq(>W5е,F9Vb! <MARmh3ʸy4BӎI>J7=^N]H 7t`*  [7 j(OMOy l}&EmgܑUGcVQtZ558['D|np&i%\s˩-Jqpnbv!GQ}S4t{m'QA.K3>2ݱA)jY:s"寞tT*SiY`|v$4\06 0&:0KF$=+ P O{>!P}h7}_Ytsg C,xN΋ J<%ղ7V 4+y>Q(Lo"Ći(!qqz6RoS`K.ZJ)P!?N^u{bY'pė8ju,A}4v?8-EVu KF3:֡](D@a~P_++g_KUY0[p%ФQ@i;lTõ$Eݙje _w:MmC&$|aeHI4 ̍p} *\ruNr7$em"jN@NZYRX׏ 38tlTQlNHn%J&3a?9Q8Y!d:K=s~OhZ=Z[~b*HX'+čDV!{eavMS3$OgZsy1ùRPB8|CJpT6Qq'TtB,J}%zb:"}v!_ĆHӜ},R#|Zy*r_U2W^0A2Qe**~iz2==1=5H`}w$O@J-6~F}o]yiY{e@Xvw>vC_p#JI?@!TLvaS+9?|'ue$U[2x'OL ;|2*_u3,رg3-H{3ӬVC2n#?ٓR=I%&NޝVͬ&Rу# " qq[bS?[6bS]x*,[j 伪\quyχb#Q}sX>MM{.Y֙>CnaW/ Z4Sx0+!)E.}9>gg-#aC^HG}PzUZm⓹\3< `R;dN 1܉W h8GڹT# DG:( k)+ s l/VJlOʡ[qL _C7ӄyM_/SoZpxUUxuPM`Eƛ5gOT'Áid/z=I=SŎJMBTf7FMú}W#4ػ0[}vNkE}M<&|[ /6昧i M/q}'"Jo#~t̎}I>E\Rl(Eӣ6u1X)Y%UKE}Q5byvpcݑ}Qu%̳D8ꓕ[6 =%6"_g ̎{F>j= ,*X%2VT=03wdBY֘jPΝ h ,!FW麟8s-i" n6&R$)ddoז>zM|8 /B,~LtLiY8ZpHN=_RT]j\@5\uGC*\}7I, qWey -&7h"DP7CMt]4Us9gAl*]*x ?/?PwY!V/(w%^%4 gp؜|[\&n;/jo6%?D ;RZ 9 hx"Fs&jp*F77$dEvkL#h2!Us)z *z }&#,ץ5 >+yVAv_L;d+f~ AtE 39gYr`]7sSUhW$q~M JX^a`OTt7ƯrpEFH>F^dӀxD~ &e%k!={TT"CQ!]txY4ӜYHpt{qn 6!6/e wvGKsw(a؃Ng0C"1e2rD~&@ 3t\>H2"yhC t{k'zğ3{d$LUfP)%wM\~ ~uѻ?p5|.'ّBN B-U[G̶^-}şR54H/7[Kgծ cQJM_[߾0hJp/N5!hKpʖmkпʶu Ҥ wpPtܯ~vۻU4R=Ï>̋ 2ٺ< vEƳ_ w:F+?zӕ;t۞ d ,~y\gL6ď,/ 8Ҩwl0,CF=X7g><-*s ٴC WVnc'X+E'xI:mkiy01NzuaÞ 4|N4=@XJh>3PQm'Sj1Rt^. 2dvtƥ`s-a |4^g\Tr86+4r$2 UP(rEIڭ;>n=$?cVm\)'W T; `̿P{*s"-d#zpRX.|Y2Z~"`(ϰgO9 }9Qqz]P j Xsi`pdP6JݪLs>xLp=i2oA9 OzuuK{9TSHuմ\QQe q^. B{]^~˜ ~?&aE~Ղ0%CBpgqK/'S=Ju,!7@0X+TAZ([VC:B Q}ij'Z'X)!Ao#ü*S{]qH6_5)HO=amb*(XnN7u{+܂JYᖹ2*(ϱW^rA~ث5곺,0e.*je1t!E-OPTȈnvBM~3m:\QF_m[h t ?*y8irC-tU_&rSl;0]V }+_~ d\3&LKUixM7qٌ5RRmʥ#3SC1UuvXs:`"<;'NÝsG^V'`>H@㣎GCRaU)w]ӋI4ڬԣAYȇ_҅BsUC[7[bQsYjArJ@oscN,w>j;OqI-(z =t%!C=vrfB98<'+T#|惾3fU? 2+$.#GaqcT2/dP!dP>`r oԐ0Ohb|Ӓ~kq.ӣ]#0mk4SU@5r;̷%]W"yw# w\gm$δ^WaTjޏLRʈ3--<с `D0W>5m&X&-5>\O[*=@F3X=n mǹ 2Tڨݣz9䓑WF8 ZZn#.R[4:U0DJɥ}lDw^GMkH=n?ޣKj;F2aڠw=dB}-"8ژ/ڬœg=x=ʊ2%OV}FgTܐT8T+x]& (GoM'#9wE{y? `wW+`i1߃ƜzO4HPHyr19#o3 vXTsgA Wɶ(jH'σp޽y|q9ww;~GPȩdSZT~?زWڡ2zXe334~/*Ywu;Q50`&),^8}M8Yېް)ѿĻ Zy4LxۣpODZ8c 3hg2*kZH#\tEaw-F}ÜnK-/pP^Ҁ&DΑDZ>єgM[? $ iBcfds1ĮZ&6[~ / :Z`o'ISI->oF7 3Ƚ>Td.0Ȑ$¾vպb":I)h)Ѣk-o.q/V @z$뙑 )nBp{Qx=Wx0Swb^M2dvRy+!+O})Y%A΋c-?DxtPL ʼnHY.d-2 qGYm }8Mc&k,e~wާ;ys:9weHk\2SӗTctD@?0Z<`3}V>Cb+qSSjZht L%|!qna57g d !CƺdWS^`cM$7wwֺ̆PeU2 !/796y#DG$Q#O,mIpބr\dfӿX;i)EMؘL$kzQo_I~=1e6%|(VE6uChr6O@+o{54 TCԲoTu9Dzs0 ?eY+B^ r?pѶ6rdd^_.7?+Rj>cʣz``oۚbX+ZUc |z~ĔhN m9x/2sbmv9[BJ1BwAWzo{jW\pyvFKTU}.Ew>+7CCnb/6?!a1}1gmD"e"RުSK Ed|`/xRN/66iCi-J@W+ʴPUOAv$B[rsTb^ӹK|+8i>2 ?ѭ '14+ &\3ǘPLrivXY,@)~7p}-#kB7GOnO@+S\Ҫfkwlrb8xPO T1(h)x=AN{ifƂ j)9+( 29b^ȗymRR',6nܹ1yETmZIo?h.SbvœoHgBS3s26): @t+RJN3L\;l¸@ lhN6Q:,[ic nrbcֶl`HRvēo 8&MAJsj.]i<(1Ö0rt-$Rh ^H3PTùá2[ 2M\LtVIz&[{^Ajh#$oC%i۔8ITAv tqY΁ P\NY H)%P1RB1WCݭ\jŵx8*DH3y9*Z}V@# %64Y@rέ$U ;{aSŤ-)S &796[8RMQzm䅇S=.C6Pߠ) 0*dp7HիC /K(\I`lu!z nr6-2ѯo,8bc ʳg:!r xGrq|?]i r"~Y}:@Y$h ͵!jl!T%]v!;Vъ_iDåuVڔ8A)_U^=*sQJY O5Hk{IiNF:gcUTKcu}h^V)nX)3_q^#}cVI^ٙ ! WU/c!$VC5(,7~I5#C͢^nMb$ՙVR,j@+nJu;|B5\Ŧ Y5k0;m&{ǟe*i|P=zVϩCy2zD4Y%AbB4]@d O#]"&] О,x{-Ji޵& ۨ8NSw}LC:1>eI}lW X1أ~Slz7Rrb~I[{9Ndh;ɐ䔝Y}G$}j1vaX΀F@Oygi%1M3mTQ6ĭف'Rz}Dv Dλn5jχw}4!e26/5\˿P갭n*<e/rS5X(Ɩۚ{蜢2B뻐!<K ڪf2<}QTsQfggҁk˨BۧM(Yq4 Cәe5Ǹ>1qӣl=`)W4U&r\xWLD ~ -wI 8@\ c O򨟘dMڸ}\M^u5w4LbZ[։g)9LGP!倊&@YvbzDxUU˧cIEB+FuL_2I@ Su2˭bƐ=aYE6*"6d6bв"rXSHɧeEM?(ۅ弨Ȟc@/Wpy,8jOTRVW pn:.K]|vFֺ%9sa,Ѣ}'8~"MŊq5)48"r jDxk2qB MU-nv4דtFPbm8uzi vR$ԋbn˷WP:|_B8,1΍ڜ}|OY ({$Zjfq4Ͼ~SfvSJsޔ+"3zڿc@Fɒ:Z5Nt]fQ|o^>\dhw!*m ]qΝĔr܈Rr& lMK=]r!vu8q|-1wOP "CAyY1GgHE30Nxb";L Á`?dlny7նɘGM9)t~2݄mpPt]+ELTщ ]ϰo~KgMKPZTCt/D862.-U"7>EFBTo}(X89Zs䠁PQ_}u_:FڋQ]$ؽb/̘zBJ| c#YMj&4ת7@5lΡsa뤇wdqdXS 5Duk3نYTH`_:1OW2^o[U&\Pr( Fy׽[K6$VBcT@K ;;eJVlyR-FayW±:Zl7: 2 On'q>?_|댫)$z!K 2jS7GY$o7?5S'Ne{2

˻/lǡiw@Ѣ^|/QxԬ:Rd(JFCiŒ)ʏC%0bn=JH H5PiD1N#h#t4 7 oh,ӈ_[Z/<. mr~ bhK7ig&2.]=~idwpsvשD=^B4hԫ~WzZvVlAҜ"y <9A{5 qY2v6Fot{"K%']뎼\1C0 c h;IR%o$@,KG<w߁Ȣ@3$رDR3XcMcѰtDc%;aNC1 Jyzs'J2/jpc9PP\ hDK;ifW6mv31|C@C%"UOU©"}2>بzt2䩒 og|~IAj۠$ Ye. >'~3X8M~5B1JHDVECVyEyjPd=%2' -Zؚ=vLt@YO,w LAёa.?c$W\%jdYDځ{^ ce׬\*[VlHtm ԽG-T0Cn(X Kw.0Y 1 wL [G)j1wm k ֈ~[ZqN(+ܟT^NV=>I*e8>Mx+$/9|x9iqGQF 1X[U@\Dw/K"7x}H|q%E:"=w~Q,CJԼ(Ћ(HRb@֘9n OԍUHWTDD5e^墾/+ɚz`E}0&`~?=s*#$hcNl{Ímll۶5m[U]սu?M0)V vvvND^IP;@mDT8uMW" FASVlJ2`u F.˛b ,| <ڷ[:~ϯz^wpk`i7[y T)QkSA5P}(ј#-OhLkpڂv0ƱUkHj"߭6Em <[}kW AJb oX| !; sAWŠp¶%"l9(ny^~Gƃc#;kATKIz7z:)$P7\wd/]gC)_">}":W~4cwj={TL=qC T,:?0ʳ[0I9^ [XbQnW:&c~n &k_p ր-ѻ#wݏbdϏpJ_px%_^_UTB~lȼ0ZUT9rJB#3_[M (Xǻ*5cU`3CUjaZ@i:f.ˏ2n^mg:Y/M`bʲWW{8}3MkE<;j(y8G#udY^yتX][#(ag !ate<;hekc~,C^,U)[Tw_>֘Q?1ݔ,{IVKm~Bלʘe( q0ڮSijgK}uk]4[Ed_0?%% =(:5`W-2cf33?' p/s] L g?.̍/`#Svy`A  +FO6뱹8’FUWP a "[)$<8#՗T@%SLSʵ,d>1%Y4|[I)t$4}E:}"=<~ׄ!O0& O.mx\ՅսK\xjZq[550xK^~>EUɥFR[R3=8O:d'j"(c6rD +NOBq&'&Sϯqfj'ȹ>*4b,z]xq.lveZNyPLZdVo4TN2Gw'$\c :Q3~&5ktՌQtb#4w!ޡ[X2jG=*L!T+fN!R> 2EsE֭ I䨻s*z|rO0—- D8OC?Y&a>fG5Ye TOeוTʻ+,-5|#;ǘ絽4*99b4Pr#J +Bx;,.)sT=>l E3D cmzJ+vOR 3^h2vf%]вB(bEwP?ܵED_lrOM*mBXZD$[H1}La\o:~̊ 4_picb[ݚ=Vl/o8GOOىsK[c<=Dr$œAy=ՔEP…Yi߂JԹ/kb;tA栄^ 9ĩ| oS{>}[ۺ7*_݄ކFzPpr878LbکGQQ0+ۉkׂ?ᵂ4p -9+Ƿ==6F[^IuBt]Tr+]/J!xW;n_21do˅QB(7dܽ.!ǿxaϘksT=k cnH>GJn'ʛ~chIH@hWΒ8QSS_]w17laO&O#<'7ÓZ N~: , |=)?_583dIJq>gh&}.ߐUMV޹Ww!)-)|)0Z2pQ焼89@]69"0}{m' =kqm6KE( N1Yn@Nu umdnP1m2{K8N}v10._l2ocW|`|p\cs5̾n׆&2=8VM"QR΁ mLq&:u'JL]{m~/r.8NKvGg;,W[Y!4t um;IAUc3)hIFt en[ `+ѿUҩ|Ib9$FN/K/}ą̜ :Nf4 ,TM|b< |H~ LAc]/[ CecC4J+/湐'!PDxboMJ顾6%1b;6Q<*xmqբߣDy@sK}n/ nI[bd19RVTI~Z<չD`D4B)+EHo`sPBz{tSձ;:6s0>@q& 3t&%#S$=}PiE"i'ޟǁ4ؿ_.?P!KNjJjW$ϹxͱyDmqc vnɨ  F%~Fr_E XI`g__0sFdYZHbv>DBz PHcBEj=32l$,_d+ePqaq }_njY(:]L]2꙽^ 9!|rjjxm4Nr>f+$BsOUH@>lL0TBfdXGun1-q>ۙ"T4]ūqGy2bj'6ݒ0zDYvff|2%9^拼AF j aOa"z ߳pF3y)_d0fCdhQPAYP6Pl㳃#>V +}>yKO;CeR)B2'b "P׼ _W $CA pʵLeniK|{7)?H@Ltـ謮#N ՛m c:3*Yo.bV \iV+.ny h;0'XST 7dgCUBsCGlMEY= @K'qTHL#Ne#G: yZZB`mהR|!f Y*O c)3wXjrݜe˴z(LX#<K1Os6Jя RKt_Vg쮍 ű{ðAAce0x+b"2"<1/. m9 #ԸHRAf/$x⡴(g4-lc,963>ڴ f6FmdP+{Sږb>䟿d.a?[3wUW$KRz$h9"z-Ck0D6X IdZ󴿾2 m(N\P/F2qN TJ6ٓd{i/& c.[Vͼa)$fhK}Z a,'OُyζJ~ ewMNBUDu}c?ђ*jYuY+ZZ)Vbo(YW8 2skӊ`t?ZJɒ!Qc1<3}Lڲ.&2v=P[2BNvvjm|LZk,\\T#xW@vKAͯ=Ү9`񛕫L- W5!Ѿ,]Vwd_wj|?w=.e.r|]P, ~ZX&BTa܎w4Qغ5#;w%A*VAe۴Pϙް8JL.ʔ5x4kiA #+R h\ ׸iʽ螺x tˎ\Qv0|mTDݨ5xE t8!.к ֕)q @J{q QnJg]w8<'S qN`vMotڠ~+6ҭڴ۩tUv?c0[ m'ت$RsN.Ljc3ͻ~[(q ?BwAEpňr@Uy)"hX؁ ,Sg &8$N:H1d|lFWlO=OJCQ)k%6DP9iȗi["ĹdMpe⑈hkM4(=^5z/)}VY6K$<,N?<雽t<0THVLM{~ƽd뤉1CȡYF-1_ب%#jv)FX҂ OFp\tj6l2$+ _A#z}0fch^ ^[i_k۝!TIъ/.N ؕl' ^=Iwl ]B5I ]&(Oߋa\asZQ'wmQ*` +1W ]~E3')W&uv%97=s)(Π{xTġt%WW^!<0ڰ4vN-`˂X!gW<W֢W4?v GDe hv3:Ӑ=q.KReA9X ovJޫ h~a@k@Df g' F$ą{P:QhL.ןZr]Rf8ѡ&( -25prXRİzped~FܞIuOfVtN)frWbW]e5]XK0.|r f N6(E[/o28S-&Nk3ܰhk E{w!U%cwF F~c|QXS5'$-GbZ'<<He&TscoBSf}z >3n78.VZyN<)b[j3}(0;mtX0hg1+AnuNK/&2LvoQnR;f`.vZ > 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 169 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 193 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.18)/Keywords() /CreationDate (D:20191204104332+01'00') /ModDate (D:20191204104332+01'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) kpathsea version 6.2.3) >> endobj 138 0 obj << /Type /ObjStm /N 72 /First 620 /Length 3447 /Filter /FlateDecode >> stream xZ[o~ׯc  qAdu*K$IfH$8O-Crgwh}! Can 7\%г*w]hO0 0j( k6CL,Rx\.B" khIӻ/w{IEVHˆSo9xV҃'D$Iׅz@w<0|AՅKL9BR@]t}hG΂]b\>`y$N;`8#` p!HyPO <`HGRc&!dPIU"0bTJHjO&B20"s]2:F21mV5iz2v@_;* _:i=>_7OVrsH5iair~:MJ[NV~Mnh0L2v0.[MT@8a-z?n9١M=8Mӧ8~rτy69~\_ ~bϛe9.QJ !Say>~_$,*+؂Ÿf䶲}A]as+K(fZ>tN d/Ҫ{M6ϖf6]F#Hԁ.G Hu%SAg ]<4$>"i1=FHO2]c"i1!bx'i=֠d41Z:L^{k;jyLaQ:EVT-_aejb8}{ևҝ{ :L^Mb81 WAt4.%<[h Ҡ`-! GOu+m4 ƏiW:':hn3 ky@YZagM 6syK贶M9pf \cmQ$r̕c:dMVZ!HFhXZIӒrTH;/CIcGGv:=g()[S9=);Kw !$>5}\_ yi!$)Ŏ,V%e_:mhJ\:htwmEYȒiUiV~w>\Tv_doo6+%.>.2wcޅ(89QuMhiEai1#57pȜt3h%r*H)9:ء|'gI)wyWu4FYAӞ*wưeh\=ad. t:Ӈ IIēE/ռR_.l^ucoGנ|x~xcaK4aGdD&Ea{*_v;Gg"n^˫%%mW0rRr G75(j4Fx&s/~x%ވCVő8ĉbYNj?ũ8|tVNʋe4mrsE8glG\&\h"./$.W?oʩ?D\73[~ޔs dzsq3]olYNj\,ĢW wZdKX5K?r>۩v0 C>އWOj8M{y!,v5w Kno{+O̾19N9[?u#ioǏ^뚊tz$wtՏɪ*ur%*vm[4鹣&p IMϠ%/.`)E'-6VFV9[lQYBCU; ۾)5Qȳɋ| Mr#qVdߪNUnjY=wu+tWV%UVk%BU(!9|-E%ҢO'Y @[:H@Rv ڡ+&t)-CP\|A]bu=NMZ+t0&iyIQ|{WD%^頓 }0/ *oFҨ˫㹍lۄoSeMpίӏ@?ǧ7۴> $xts Շ0\lP$bmh}^!iL hJ}ZXwwwڜ}[[ sk*/@U゜ƷC^{.'ȧcQ %_*fvp7 WO5^DԬ KkO56oĶ7`q+(MxaAOgg|Shxb6[NQj?wT}s{r1T}0}2Y% ?ŰNaS[Q_=_;t(3\3̸ف/ sb3uTIG_YŲ > iLf6d1!InoaF1NF)0fw 1U? <-Ine2PR(H-GgINh838uj`rU#ytm:?C@ 9 MQ(nTQ8pdB&ڪ:0%{.G!eh2tFM-mj`b1r9 dq9 #ck6 +MbmBPQh7n1*Dz fLPfSFRvl >c-ҸRQQ@G2 [y#@Jsћ  \nWG{ĩl!l¹8ҔqD q*p13 FYڒM$H\ufX4ߴɔuN4Sנ8dJmvm Ph.ʳx6鎐DE}l ֬5X RZYm[ϫ=#J\U+SU,lu= ] /Length 468 /Filter /FlateDecode >> stream x:ayWa- dR)D$ Q&\Gp01gҠI:.'?o3,wc*39$hS*1A1kP`yXwll_ y&B܂Cdjx.47 yS B  ]p!!GK!̉3x9Pp+?cO!x'o<'< Xn?C0Vwwaf`^t7 1-+#7/;nGk6a Wf+6  mR(JS晪x m0TETETETETEU=EUDUUiq]~SV>vO'1=]^LItR/E"}H_>E"x^/) Uf=f]?lZn endstream endobj startxref 204529 %%EOF phylonium-1.2/documentation/manual.tex000066400000000000000000000247031357170176200202670ustar00rootroot00000000000000\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 has two runtime prerequisites: \tool{libdivsufsort} and the \tool{GNU Scientific Library} (GSL). Install them via your package manager, for instance \lstinline!apt!. \begin{lstlisting} % sudo apt install libdivsufsort libgsl23 \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 libgsl-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.2/libs/000077500000000000000000000000001357170176200143425ustar00rootroot00000000000000phylonium-1.2/libs/Makefile.am000066400000000000000000000023531357170176200164010ustar00rootroot00000000000000noinst_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.2/libs/compat-stdlib.h000066400000000000000000000002601357170176200172530ustar00rootroot00000000000000/** * 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.2/libs/pfasta.c000066400000000000000000000374721357170176200160010ustar00rootroot00000000000000/* * Copyright (c) 2015-2018, 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 "v14+2" #ifdef __SSE2__ #include #endif #ifdef __STDC_NO_THREADS__ #define thread_local #else #include #endif /** 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); while (LIKELY(isalpha(buffer_peek(pp)))) { 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.2/libs/pfasta.h000066400000000000000000000055141357170176200157760ustar00rootroot00000000000000/* * Copyright (c) 2015-2018, 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); #ifdef __STDC_NO_THREADS__ /** If the preprocessor macro `PFASTA_NO_THREADS` is defined, the parser is not * fully thread safe. */ #define PFASTA_NO_THREADS #endif #ifdef __cplusplus } #endif #endif /* PFASTA_H */ phylonium-1.2/libs/reallocarray.c000066400000000000000000000023671357170176200171760ustar00rootroot00000000000000#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.2/libs/revseqcmp.c000066400000000000000000000030131357170176200165100ustar00rootroot00000000000000/** * 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.2/libs/revseqcmp.h000066400000000000000000000015251357170176200165230ustar00rootroot00000000000000/** * 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.2/libs/revseqcmp_avx2.c000066400000000000000000000027511357170176200174600ustar00rootroot00000000000000/** * 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.2/libs/revseqcmp_ssse3.c000066400000000000000000000025411357170176200176350ustar00rootroot00000000000000/** * 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.2/libs/seqcmp.c000066400000000000000000000031351357170176200160000ustar00rootroot00000000000000/** * 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.2/libs/seqcmp.h000066400000000000000000000012031357170176200157770ustar00rootroot00000000000000/** * 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.2/libs/seqcmp_avx2.c000066400000000000000000000026341357170176200167430ustar00rootroot00000000000000/** * 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.2/libs/seqcmp_avx512.c000066400000000000000000000020351357170176200171040ustar00rootroot00000000000000/** * 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.2/libs/seqcmp_sse2.c000066400000000000000000000026031357170176200167330ustar00rootroot00000000000000/** * 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.2/m4/000077500000000000000000000000001357170176200137315ustar00rootroot00000000000000phylonium-1.2/m4/ax_cxx_compile_stdcxx_11.m4000066400000000000000000000456471357170176200211130ustar00rootroot00000000000000# =========================================================================== # 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.2/m4/ax_gcc_func_attribute.m4000066400000000000000000000204101357170176200205120ustar00rootroot00000000000000# =========================================================================== # 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.2/man/000077500000000000000000000000001357170176200141645ustar00rootroot00000000000000phylonium-1.2/man/phylonium.1.in000066400000000000000000000053261357170176200167050ustar00rootroot00000000000000.TH PHYLONIUM "1" "2019-08-19" "@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 - 2019 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 .SH BUGS .SS Reporting Bugs Please report bugs to . phylonium-1.2/src/000077500000000000000000000000001357170176200142005ustar00rootroot00000000000000phylonium-1.2/src/Makefile.am000066400000000000000000000015671357170176200162450ustar00rootroot00000000000000bin_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.2/src/esa.cxx000066400000000000000000000327741357170176200155110ustar00rootroot00000000000000/** * 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.2/src/esa.h000066400000000000000000000054731357170176200151320ustar00rootroot00000000000000/** * 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.2/src/evo_model.cxx000066400000000000000000000074061357170176200167040ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ #include "evo_model.h" #include #include #include #include #include #include #include #include "revseqcmp.h" #include "seqcmp.h" gsl_rng *RNG; /** @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::array p = {subst_rate, 1 - subst_rate}; std::array neu = {}; gsl_ran_multinomial(RNG, 2, homologs, p.data(), reinterpret_cast(neu.data())); ret.substitutions = neu[0]; 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.2/src/evo_model.h000066400000000000000000000030041357170176200163170ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © Fabian Klötzl */ #pragma once #include #include #include #include #include #include extern gsl_rng *RNG; /** @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.2/src/global.h000066400000000000000000000023061357170176200156120ustar00rootroot00000000000000/** * 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 }; extern int FLAGS; extern int THREADS; extern int RETURN_CODE; extern double ANCHOR_P_VALUE; extern long unsigned int BOOTSTRAP; /** * @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.2/src/io.cxx000066400000000000000000000143441357170176200153410ustar00rootroot00000000000000/** * 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.find('.', left); right = (right == std::string::npos) ? s_file_name.size() : right; // 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.2/src/io.h000066400000000000000000000011011357170176200147510ustar00rootroot00000000000000/** * 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.2/src/phylonium.cxx000066400000000000000000000247401357170176200167570ustar00rootroot00000000000000/** * SPDX-License-Identifier: GPL-3.0-or-later * Copyright 2018 - 2019 © 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 "config.h" #include "global.h" #include "io.h" #include "process.h" #include "sequence.h" #ifdef _OPENMP #include #endif extern gsl_rng *RNG; double ANCHOR_P_VALUE = 0.025; int FLAGS = flags::none; int THREADS = 1; long unsigned int BOOTSTRAP = 0; int RETURN_CODE = EXIT_SUCCESS; // 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); int main(int argc, char *argv[]) { RNG = gsl_rng_alloc(gsl_rng_default); if (!RNG) { err(1, "RNG allocation failed."); } // seed the random number generator with the current time // TODO: enable seeding for reproducibility gsl_rng_set(RNG, time(NULL)); 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:hr: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 '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 (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) 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" " -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.2/src/process.cxx000066400000000000000000000431171357170176200164100ustar00rootroot00000000000000/** * 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 "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); } ////////////////////////////// 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; } 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.2/src/process.h000066400000000000000000000074341357170176200160370ustar00rootroot00000000000000/** * 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.2/src/sequence.cxx000066400000000000000000000072111357170176200165350ustar00rootroot00000000000000/** * 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.2/src/sequence.h000066400000000000000000000066111357170176200161650ustar00rootroot00000000000000/** * 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.2/test/000077500000000000000000000000001357170176200143705ustar00rootroot00000000000000phylonium-1.2/test/Makefile.am000066400000000000000000000021171357170176200164250ustar00rootroot00000000000000check_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.2/test/Tmain.cxx000066400000000000000000000003531357170176200161650ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include "catch.hpp" #include "global.h" 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; phylonium-1.2/test/Tprocess.cxx000066400000000000000000000065711357170176200167270ustar00rootroot00000000000000#include #include "catch.hpp" #include "process.h" 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.2/test/Tsequence.cxx000066400000000000000000000020441357170176200170500ustar00rootroot00000000000000#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.2/test/catch.hpp000066400000000000000000022711361357170176200161770ustar00rootroot00000000000000/* * Catch v2.9.2 * Generated: 2019-08-08 13:35:12.279703 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2019 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 9 #define CATCH_VERSION_PATCH 2 #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 #ifdef __APPLE__ # include # if TARGET_OS_OSX == 1 # define CATCH_PLATFORM_MAC # elif 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 #if defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS #endif #ifdef __clang__ # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ _Pragma( "clang diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic push" ) \ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic pop" ) #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 #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++ #ifdef _MSC_VER # if _MSC_VER >= 1900 // Visual Studio 2015 or newer # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS # endif // 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(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) # define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR # endif #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 //////////////////////////////////////////////////////////////////////////////// // Check if string_view is available and usable // The check is split apart to work around v140 (VS2015) preprocessor issue... #if defined(__has_include) #if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW #endif #endif //////////////////////////////////////////////////////////////////////////////// // Check if optional is available and usable #if defined(__has_include) # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // __has_include //////////////////////////////////////////////////////////////////////////////// // Check if byte is available and usable #if defined(__has_include) # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_BYTE # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // __has_include //////////////////////////////////////////////////////////////////////////////// // Check if variant is available and usable #if defined(__has_include) # 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 // __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_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) # define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS #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_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_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; 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_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS // 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 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. c_str() must return a null terminated /// string, however, and so the StringRef will internally take ownership /// (taking a copy), if necessary. In theory this ownership is not externally /// visible - but it does mean (substring) StringRefs should not be shared between /// threads. class StringRef { public: using size_type = std::size_t; private: friend struct StringRefTestAccess; char const* m_start; size_type m_size; char* m_data = nullptr; void takeOwnership(); static constexpr char const* const s_empty = ""; public: // construction/ assignment StringRef() noexcept : StringRef( s_empty, 0 ) {} StringRef( StringRef const& other ) noexcept : m_start( other.m_start ), m_size( other.m_size ) {} StringRef( StringRef&& other ) noexcept : m_start( other.m_start ), m_size( other.m_size ), m_data( other.m_data ) { other.m_data = nullptr; } StringRef( char const* rawChars ) noexcept; 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() ) {} ~StringRef() noexcept { delete[] m_data; } auto operator = ( StringRef const &other ) noexcept -> StringRef& { delete[] m_data; m_data = nullptr; m_start = other.m_start; m_size = other.m_size; return *this; } operator std::string() const; void swap( StringRef& other ) noexcept; public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; auto operator != ( StringRef const& other ) const noexcept -> bool; auto operator[] ( size_type index ) const noexcept -> char; public: // named queries auto empty() const noexcept -> bool { return m_size == 0; } auto size() const noexcept -> size_type { return m_size; } auto numberOfCharacters() const noexcept -> size_type; auto c_str() const -> char const*; public: // substrings and searches auto substr( size_type start, size_type size ) const noexcept -> StringRef; // Returns the current start pointer. // Note that the pointer can change when if the StringRef is a substring auto currentData() const noexcept -> char const*; private: // ownership queries - may not be consistent between calls auto isOwned() const noexcept -> bool; auto isSubstring() const noexcept -> bool; }; auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } } // namespace Catch inline 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_type_traits.hpp #include namespace Catch{ #ifdef CATCH_CPP17_OR_GREATER template inline constexpr auto is_unique = std::true_type{}; template inline constexpr auto is_unique = std::bool_constant< (!std::is_same_v && ...) && is_unique >{}; #else template struct is_unique : std::true_type{}; template struct is_unique : std::integral_constant ::value && is_unique::value && is_unique::value >{}; #endif } // end catch_type_traits.hpp // 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, _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 L1, typename...E1, template class L2, typename...E2> \ constexpr auto append(L1, L2) noexcept -> L1 { return {}; }\ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ constexpr auto append(L1, L2, Rest...) noexcept -> decltype(append(L1{}, Rest{}...)) { return {}; }\ template< template class L1, typename...E1, typename...Rest>\ constexpr auto append(L1, TypeList, Rest...) noexcept -> L1 { return {}; }\ \ template< template class Container, template class List, typename...elems>\ constexpr auto rewrap(List) noexcept -> TypeList> { return {}; }\ template< template class Container, template class List, class...Elems, typename...Elements>\ constexpr auto rewrap(List,Elements...) noexcept -> decltype(append(TypeList>{}, rewrap(Elements{}...))) { return {}; }\ \ template