kissplice-2.4.0-p1/AUTHORS000644 000765 000024 00000000721 12642755506 015204 0ustar00ishistaff000000 000000 Vincent Lacroix (2011-) Leandro Lima (2014-) Gustavo Sacomoto (2011-2014) Pierre Peterlongo (2011-) Rayan Chikhi (2011-) Vincent Miele (2012-) Alice Julien-Laferrière (2012-2013) Camille Marchet (2013-) With the valuable help of Janice Kielbassa, Marie-France Sagot, David Parsons, Raluca Uricaru, Pavlos Antoniou and Gunter Roeth Contact: Vincent Lacroix , kissplice-users@lists.gforge.inria.fr kissplice-2.4.0-p1/ChangeLog000644 000765 000024 00000026337 12703731714 015712 0ustar00ishistaff000000 000000 2016-04-14 Leandro Ishi * 2.4.0 -> 2.4.0-p1 Bugs fixes: -Fixed a bug that happened if the user uses the --min_overlap parameter. 2015-10-22 Leandro Ishi * 2.3.1 -> 2.4.0 Bugs fixes: -Fixed a minor bug in which some branching bubbles were wrongly compressed in the 4-nodes compression step. This caused some rare bubbles to be missed. -Fixed a minor bug in which some linear paths remained uncompressed after the 4-nodes compression step. This caused some rare bubbles to be missed. -Fixed a bug which caused the reads in the junction AS, SB, ASSB, AB and S to be wrongly counted and another which caused a read to map to a bubble without enough overlapping bases. This may change the counts of the events and their classification whether coherent or uncoherent. -Fixed the main script not recognizing when the stack size limit was exceeded and to advise the user to execute “ulimit -s unlimited” before rerunning KisSplice. New features: -Modified the -s parameter. -s now should be always followed by an integer and it changes which types of SNPs will be output. If 0 (default), will not output SNPs. If 1, will output Type0a-SNPs. If 2, will output Type0a and Type0b SNPs. -Added a new experimental algorithm that searches for bubbles by listing all paths. The time and memory that may be used by this new algorithm is not bounded by a polynomial function on the input size, but it worked well on some instances in practice. To use this algorithm instead of the default one, it is needed to call KisSplice with parameter --experimental. -Added a memory limit to the experimental algorithm (parameter --max-memory). When using the experimental algorithm (--experimental) instead of the default one, the user may give a memory limit to this algorithm. If the algorithm uses more than this limit, it is killed. By default, this value is unlimited (i.e. no memory limit is imposed); -Added --keep-bccs parameter which is a boolean that indicates if KisSplice should keep the node/edges files for all bccs after execution (defaults to false). -Added --output-branch-count parameter which is a boolean that indicates if KisSplice should output the number of branching nodes in each path (defaults to false). -Added --keep-counts parameter which is a boolean that indicates if KisSplice should keep the .counts file after the sequencing-errors-removal step (defaults to false. We added this because the .counts file was too big sometimes and generally the user did not need to keep it (if needed, it can be recreated with -C and --keep-counts)) -Added a timestamp to the main messages of the pipeline and a summary log file on the results folder Improvements: -Improved the Sequencing error removal step (e.g.: sometimes the files output by the error removal step are already calculated, so we do not need to redo them); -Improved the way KisSplice classifies bubbles: 1) indels (Type 3) now have a shorter path length of at maximum 2k (or 2k-2, without counting the two characters of context); 2) now the maximum 10% hamming distance to classify a bubble as Type_0b considers only the variable part of the upper and lower path. -Bubbles are now output before and after read coherency module. Parameters: -m (LL_MIN) from 2k-8 to 2k-10 (not counting the two nucleotides of context. If you count them, it was from 2k-6 to 2k-8). So, more bubbles will be found now. -M (UL_MAX) from 10000 to 1000000. This enables KisSplice to find very long and rare events, and it does not impact much in runtime. --timeout now defaults to 100000s -C from 0.02 to 0.05 2014-12-17 Camille Marchet * 2.2.1 -> 2.3.0 Bugs fixed : - Reads containing only 'N': the graph construction was stopped if the file contained a read composed only of 'N's. This is was a silence bug, no error message was produced. - Problems compiling with new versions of MAC OSX (10.8+): KisSplice was not compiling with the new default C++ compiler of OSX 10.8+. Improvements : -KissReads : * speed up x2 on average * More precise control of KissReads' behaviour by decorrelating the values of k and of the seed. * -m option was changed in order to return the Id of reads that mapped on a path Parameters : --timeout default now at 10000 2014-07-24 Camille Marchet * 2.2.0 -> 2.2.1 This version fixes a bug in KissReads when a fasta/fastq file contains empty lines. Indeed KisSplice and KissReads had not the same way to treat empty lines, KisSplice part was just ignoring them while KissReads stopped the mapping at the first line of this type. As a result, many events were classified as "incoherent", because KissReads did not check on them. They are now correctly classified. It also corrects CMAKE behavior for ZLIB library. This affects only the compilation & installation of KisSplice in some cases and does not have impact on KiSplice results. 2014-06-05 Camille Marchet * 2.1.0 -> 2.2.0 Pipeline features : - New enumeration algorithm : restricted enumeration branch has been merged and former enumeration is no longer used. This new enumeration has been developped to tackle the issue of events lost in big bccs. The idea is to enumerate all bubbles with at most b branching nodes in each path, doing so we avoid repeated regions. Options and parameters : - Do not output SNPs by default. The flag (-s) outputs SNPs. - The maximum number of cycles (max_cycles) increased from 10k to 100M. - The timeout increased from 900s to 3600s. - Option -u goes back to not returning unfinished bcc by default. - -b option to set the number of branches, related to the new enumeration method(5 by default). For developpers : - smaller data for tests 2014-01-22 Camille Marchet * 2.0.0 -> 2.1.0 Pipeline features : - Bug fixed : -Error removal step erased random edges. Now fixed : discards edges according to C value. - "N" nucleotides are now ignored by the pipeline during graph building. - Now working with Minia r6025 and Minigraph r5097. - Now using 4 Bloom filters by default. Options and parameters : - New option --mismatches makes possible to allow some mismatches while KissReads' reads alignment on results. - Option -u has changed: =1 to store unfinished bcc (default) =0 to discard unfinished bcc - Default parameters have changed : k moves from 25 to 41 and M from 1000 to 10000. Outputs : - The command line is returned in the output (.o) file. - Type_0 results file now presents two types of variants : Type_0a where only one single nucleotide change is returned in the upper path Type_0b where several single nucleotide changes are returned in the upper path For developpers : - 3 Functional tests added : test each module (ks_error_removal, ks_run_module, ks_bubble_enumeration). - A test on debruijn graph construction added - It is possible to perfom profiling on KisSplice using gprof 2013-11-13 Alice Julien-Laferriere * 1.8.3 -> 2.0 -New kissreads version following developments of symbiose project: specific counts for kissplice. 4 counts are made now : ASB, AS, SB ,S, AB (for events: ASB, AB). parallel version by default -Coherence is made based on reads covering nt not on reads covering kmer. min_overlap parameters is added. A read is count only if it overlap the variable part if the events by min_overlap nt. eg: if the read overlap by min_overlap S it is count in S, by min_overlap the junction AS, etc. -New counting option for junction are therefore in kissplice.py scripts: options --counts : 0 (counts are reported as CX), 1 ( ASX, SBX, ASSBX, ABX), 2 ( ASX, SBX, SX, ASSB, ABX). See user_guide for full information. -There are k nucleotide sequences on both sides of the variations (instead of k-1 in previous version) - New options : --output-context and --output-path --output-context : if is possible, KisSplice will output the longest sequences possible before and after the events, the outputted context is in lower case --output-path: kissplice output in all_path_k{k value} file the nodes ids of the cycles -The counts are made for the SNPs with a seed of k + 1 because of the k nt context - It is now forbidden to pick an even value for k -Changes the results, they are more numerous due to distinction of switching node enable by the k nt context. Counts can be modified a little bit, because of the seed of k+1 nt for nt and the min_overlap parameters. -Efficient internal use of disk capacities. Suitable for a large range of systems from laptops to clusters. Implemented by Gunter Roeth (Bull HPC Extreme Computing ) 2013-08-02 David Parsons * 1.8.2 -> 1.8.3 New build organization: main binary kissplice remains in the bin directory, all the secondary binaries now go to lib/kissplice 2013-06-03 Vincent Miele * 1.8.1 -> 1.8.2 New kissreads version, more stable compressed reads are now valid input 2013-04-04 Alice Julien-Laferriere * 1.8.0 -> 1.8.1 Patch: it is now possible to compile and use k-mer > 31 (use cmake . -DKMERS_OVER_32=ON) -C option: default to 0.02 2013-03-28 Alice Julien-Laferriere * 1.7.1 -> 1.8.0 De Bruijn Graph construction use new Minia version (v4) much more memory efficient Dynamic error removal: a pre-processing step to remove edges with low relative coverage, which are likely sequencing errors. Uniform criteria to stop the enumeration: if a bcc reached the timeout OR the maximum number of cycles, nothing is output. This should change the results. 2012-09-01 Gustavo Sacomoto * 1.6.3 -> 1.7 Python script kissplice replaces shell script kissplice.sh 2012-04-04 Gustavo Sacomoto * 1.6.2 -> 1.6.3 Bug related to mac OS fixed 2012-03-29 Gustavo Sacomoto * 1.6 -> 1.6.2 Large k (k>32) bug fixed Proper installation using cmake / done by Vincent Miele 2012-02-14 Pierre Peterlongo * 1.5.1 -> 1.6 Possibility to chose the output directory (-o) Possibility to run kissplice from any directory Possibility to avoid the computation of SNPs and sequencing errors (-s) Updated manual 2012-02-03 Gustavo Sacomoto * 1.5 -> 1.5.1 Small bug fixed 2012-01-23 Pierre Peterongo * 1.4 -> 1.5 ranking and sorting results on read coverage and not k-mer coverage provides 5 files: uncoherent (a bit useless) Type 0: SNPS / sequencing errors Type 1: splicing events Type 2: Tandem repeats Type 3: Short indels 2012-01-17 Pierre Peterlongo * 1.2 -> 1.4 Possibility to use k larger than 32 nt (slower) or smaller than 32 (faster) 2012-01-16 Pierre Peterlongo * 1.1 -> 1.2 Better de-bruijn graph creation Read length unlimited Checks the read coherency and provides read coverage on results 2011-10-28 Gustavo Sacomoto * 1.0 -> 1.1 Fixed small bugs Improved the user guide 2011-10-24 Pierre Peterlongo * 1.0 Initial release kissplice-2.4.0-p1/CMakeLists.txt000644 000765 000024 00000016110 12703731765 016672 0ustar00ishistaff000000 000000 # ============================================================================ # Require minimal version of cmake # ============================================================================ CMAKE_MINIMUM_REQUIRED(VERSION 2.8.5) # ============================================================================ # Set project name and languages # ============================================================================ PROJECT(kissplice CXX C) # ============================================================================ # Get GNU standard installation directories (GNUInstallDirs module) # ============================================================================ INCLUDE(GNUInstallDirs) # ============================================================================ # Set compilation flags # ============================================================================ IF(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE Distribution) ENDIF(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall") SET(CMAKE_C_FLAGS_DEBUG "-g -Wall") SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall") SET(CMAKE_C_FLAGS_RELEASE "-O3 -Wall") SET(CMAKE_CXX_FLAGS_DISTRIBUTION "-O3 -w") SET(CMAKE_C_FLAGS_DISTRIBUTION "-O3 -w") SET(CMAKE_CXX_FLAGS_PROFILING "-g -pg") SET(CMAKE_C_FLAGS_PROFILING "-g -pg") # ============================================================================ # Manage the KMERS_OVER_32 option # The default value for KMERS_OVER_32 is # * ON on 64 bits systems # * OFF on 32 bits systems # If a user tries to set KMERS_OVER_32 to ON on a 32 bits system, it will be # forced back to OFF in the CACHE and a FATAL_ERROR message will be issued. # ============================================================================ SET(KMERS_OVER_32_HELP_STRING "Increase the maximum k-mer size on 64-bits systems") IF (${CMAKE_SIZEOF_VOID_P} LESS 8 ) set(KMERS_OVER_32 OFF CACHE BOOL KMERS_OVER_32_HELP_STRING) IF (KMERS_OVER_32) set(KMERS_OVER_32 OFF CACHE BOOL KMERS_OVER_32_HELP_STRING FORCE) MESSAGE(FATAL_ERROR "The KMERS_OVER_32 option cannot be used on 32 bits systems") ENDIF () ELSE () set(KMERS_OVER_32 ON CACHE BOOL KMERS_OVER_32_HELP_STRING) ENDIF () # ============================================================================ # Tell cmake where to put binary files. # By GNU standards "executable programs that users can run" should go in # bindir a.k.a ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR} # and "executable programs to be run by other programs rather than by users" # in libexecdir a.k.a ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR} # # The main script of kissplice (kissplice.py) must be configured to find the # secondary binaries. These can be found either in libexecdir (after install) # or in the build-tree, we hence need 2 versions of kissplice: one that uses # those secondary binaries in the build-tree and one that is installable. # The latter will be placed in an ad-hoc directory called "install" in the # build-tree # # Since all the files to be *compiled* belong to libexecdir, we can set # ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} once and for all her # ============================================================================ # Set main binary dir SET(MAIN_BIN_DIR ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) # Set the location of other executables SET(SEC_BIN_DIR ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBEXECDIR}/kissplice) # Tell cmake where to put compiled files SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${SEC_BIN_DIR}) # Make a configured copy of kissplice main (python) file # KS_SEC_EXEC_PATH must be a relative path to support local installations using # make install DESTDIR=/some/path set(KS_SEC_EXEC_PATH ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) configure_file(${PROJECT_SOURCE_DIR}/kissplice.py ${MAIN_BIN_DIR}/kissplice @ONLY) # Tell cmake where to install the main script (kissplice) install(PROGRAMS ${MAIN_BIN_DIR}/kissplice DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) # ============================================================================ # zlib required for kissreads # ============================================================================ FIND_PACKAGE(ZLIB REQUIRED) # ============================================================================ # Tell cmake about subdirectories to look into # ============================================================================ ADD_SUBDIRECTORY(debruijn-v4) ADD_SUBDIRECTORY(thirdparty) ADD_SUBDIRECTORY(modules) ADD_SUBDIRECTORY(man) ADD_SUBDIRECTORY(doc) # ============================================================================ # Add testing capabilities # ============================================================================ ENABLE_TESTING() ADD_SUBDIRECTORY(tests) # ============================================================================ # Adds the 'dist' target (that will use cpack) # ============================================================================ add_custom_target(dist COMMAND ${CMAKE_BUILD_TOOL} package_source) # ============================================================================ # Add custom uninstall target # ============================================================================ configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/utils/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) # ============================================================================ # Configure cpack # ============================================================================ SET(CPACK_PACKAGE_NAME "kissplice") SET(CPACK_PACKAGE_VENDOR "Kissplice Development Team") SET(CPACK_PACKAGE_VERSION_MAJOR "2") SET(CPACK_PACKAGE_VERSION_MINOR "4") SET(CPACK_PACKAGE_VERSION_PATCH "0-p1") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Splicing events caller") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") SET(CPACK_RESOURCE_FILE_AUTHORS "${CMAKE_SOURCE_DIR}/AUTHORS") SET(CPACK_RESOURCE_FILE_INSTALL "${CMAKE_SOURCE_DIR}/INSTALL") SET(CPACK_SOURCE_GENERATOR "TGZ") SET(CPACK_SOURCE_IGNORE_FILES "README.CMake" "README.Licence" "README.Release" "README.NamingConventions" "README.Kissreads" "CMakeFiles" "Makefile" "_CPack_Packages" "CMakeCache.txt" ".*\\\\.svn" ".*\\\\.idea" ".*\\\\.DS_Store" ".*\\\\.gz" ".*\\\\~" ".*\\\\.o" "bcc/" "debruijn/" "debruijn-v2" "datasets" "ismb" "Recomb" "RR" "script_tarjan_aphid.sh" "splice_sites_results" "splicing_events" "TODO.*" "Validation" "validation_script.sh" ".*project" "/\\\\.git*" "results" "build" "incremental-bicon.ps" "scripts" "skeletons" "doc/dev" "alt_splicing_sample_ex.png" "quantifModel-big.png" "resType0.png" "resType1.png" "quantifModel.png" "ucsc.png" "user_guide.aux" "user_guide.bib" "user_guide.log" "user_guide.toc" "user_guide.tex" "jobim13" ${CPACK_SOURCE_IGNORE_FILES} ) SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") include(CPack) kissplice-2.4.0-p1/COPYING000644 000765 000024 00000124376 12575016362 015177 0ustar00ishistaff000000 000000 CeCILL FREE SOFTWARE LICENSE AGREEMENT (english version, french version below) Notice This Agreement is a Free Software license agreement that is the result of discussions between its authors in order to ensure compliance with the two main principles guiding its drafting: * firstly, compliance with the principles governing the distribution of Free Software: access to source code, broad rights granted to users, * secondly, the election of a governing law, French law, with which it is conformant, both as regards the law of torts and intellectual property law, and the protection that it offers to both authors and holders of the economic rights over software. The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) license are: Commissariat à l'Energie Atomique - CEA, a public scientific, technical and industrial research establishment, having its principal place of business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. Centre National de la Recherche Scientifique - CNRS, a public scientific and technological establishment, having its principal place of business at 3 rue Michel-Ange, 75794 Paris cedex 16, France. Institut National de Recherche en Informatique et en Automatique - INRIA, a public scientific and technological establishment, having its principal place of business at Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex, France. Preamble The purpose of this Free Software license agreement is to grant users the right to modify and redistribute the software governed by this license within the framework of an open source distribution model. The exercising of these rights is conditional upon certain obligations for users so as to preserve this status for all subsequent redistributions. In consideration of access to the source code and the rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors only have limited liability. In this respect, the risks associated with loading, using, modifying and/or developing or reproducing the software by the user are brought to the user's attention, given its Free Software status, which may make it complicated to use, with the result that its use is reserved for developers and experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the suitability of the software as regards their requirements in conditions enabling the security of their systems and/or data to be ensured and, more generally, to use and operate it in the same conditions of security. This Agreement may be freely reproduced and published, provided it is not altered, and that no provisions are either added or removed herefrom. This Agreement may apply to any or all software for which the holder of the economic rights decides to submit the use thereof to its provisions. Article 1 - DEFINITIONS For the purpose of this Agreement, when the following expressions commence with a capital letter, they shall have the following meaning: Agreement: means this license agreement, and its possible subsequent versions and annexes. Software: means the software in its Object Code and/or Source Code form and, where applicable, its documentation, "as is" when the Licensee accepts the Agreement. Initial Software: means the Software in its Source Code and possibly its Object Code form and, where applicable, its documentation, "as is" when it is first distributed under the terms and conditions of the Agreement. Modified Software: means the Software modified by at least one Contribution. Source Code: means all the Software's instructions and program lines to which access is required so as to modify the Software. Object Code: means the binary files originating from the compilation of the Source Code. Holder: means the holder(s) of the economic rights over the Initial Software. Licensee: means the Software user(s) having accepted the Agreement. Contributor: means a Licensee having made at least one Contribution. Licensor: means the Holder, or any other individual or legal entity, who distributes the Software under the Agreement. Contribution: means any or all modifications, corrections, translations, adaptations and/or new functions integrated into the Software by any or all Contributors, as well as any or all Internal Modules. Module: means a set of sources files including their documentation that enables supplementary functions or services in addition to those offered by the Software. External Module: means any or all Modules, not derived from the Software, so that this Module and the Software run in separate address spaces, with one calling the other when they are run. Internal Module: means any or all Module, connected to the Software so that they both execute in the same address space. GNU GPL: means the GNU General Public License version 2 or any subsequent version, as published by the Free Software Foundation Inc. Parties: mean both the Licensee and the Licensor. These expressions may be used both in singular and plural form. Article 2 - PURPOSE The purpose of the Agreement is the grant by the Licensor to the Licensee of a non-exclusive, transferable and worldwide license for the Software as set forth in Article 5 hereinafter for the whole term of the protection granted by the rights over said Software. Article 3 - ACCEPTANCE 3.1 The Licensee shall be deemed as having accepted the terms and conditions of this Agreement upon the occurrence of the first of the following events: * (i) loading the Software by any or all means, notably, by downloading from a remote server, or by loading from a physical medium; * (ii) the first time the Licensee exercises any of the rights granted hereunder. 3.2 One copy of the Agreement, containing a notice relating to the characteristics of the Software, to the limited warranty, and to the fact that its use is restricted to experienced users has been provided to the Licensee prior to its acceptance as set forth in Article 3.1 hereinabove, and the Licensee hereby acknowledges that it has read and understood it. Article 4 - EFFECTIVE DATE AND TERM 4.1 EFFECTIVE DATE The Agreement shall become effective on the date when it is accepted by the Licensee as set forth in Article 3.1. 4.2 TERM The Agreement shall remain in force for the entire legal term of protection of the economic rights over the Software. Article 5 - SCOPE OF RIGHTS GRANTED The Licensor hereby grants to the Licensee, who accepts, the following rights over the Software for any or all use, and for the term of the Agreement, on the basis of the terms and conditions set forth hereinafter. Besides, if the Licensor owns or comes to own one or more patents protecting all or part of the functions of the Software or of its components, the Licensor undertakes not to enforce the rights granted by these patents against successive Licensees using, exploiting or modifying the Software. If these patents are transferred, the Licensor undertakes to have the transferees subscribe to the obligations set forth in this paragraph. 5.1 RIGHT OF USE The Licensee is authorized to use the Software, without any limitation as to its fields of application, with it being hereinafter specified that this comprises: 1. permanent or temporary reproduction of all or part of the Software by any or all means and in any or all form. 2. loading, displaying, running, or storing the Software on any or all medium. 3. entitlement to observe, study or test its operation so as to determine the ideas and principles behind any or all constituent elements of said Software. This shall apply when the Licensee carries out any or all loading, displaying, running, transmission or storage operation as regards the Software, that it is entitled to carry out hereunder. 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS The right to make Contributions includes the right to translate, adapt, arrange, or make any or all modifications to the Software, and the right to reproduce the resulting software. The Licensee is authorized to make any or all Contributions to the Software provided that it includes an explicit notice that it is the author of said Contribution and indicates the date of the creation thereof. 5.3 RIGHT OF DISTRIBUTION In particular, the right of distribution includes the right to publish, transmit and communicate the Software to the general public on any or all medium, and by any or all means, and the right to market, either in consideration of a fee, or free of charge, one or more copies of the Software by any means. The Licensee is further authorized to distribute copies of the modified or unmodified Software to third parties according to the terms and conditions set forth hereinafter. 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION The Licensee is authorized to distribute true copies of the Software in Source Code or Object Code form, provided that said distribution complies with all the provisions of the Agreement and is accompanied by: 1. a copy of the Agreement, 2. a notice relating to the limitation of both the Licensor's warranty and liability as set forth in Articles 8 and 9, and that, in the event that only the Object Code of the Software is redistributed, the Licensee allows future Licensees unhindered access to the full Source Code of the Software by indicating how to access it, it being understood that the additional cost of acquiring the Source Code shall not exceed the cost of transferring the data. 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE When the Licensee makes a Contribution to the Software, the terms and conditions for the distribution of the resulting Modified Software become subject to all the provisions of this Agreement. The Licensee is authorized to distribute the Modified Software, in source code or object code form, provided that said distribution complies with all the provisions of the Agreement and is accompanied by: 1. a copy of the Agreement, 2. a notice relating to the limitation of both the Licensor's warranty and liability as set forth in Articles 8 and 9, and that, in the event that only the object code of the Modified Software is redistributed, the Licensee allows future Licensees unhindered access to the full source code of the Modified Software by indicating how to access it, it being understood that the additional cost of acquiring the source code shall not exceed the cost of transferring the data. 5.3.3 DISTRIBUTION OF EXTERNAL MODULES When the Licensee has developed an External Module, the terms and conditions of this Agreement do not apply to said External Module, that may be distributed under a separate license agreement. 5.3.4 COMPATIBILITY WITH THE GNU GPL The Licensee can include a code that is subject to the provisions of one of the versions of the GNU GPL in the Modified or unmodified Software, and distribute that entire code under the terms of the same version of the GNU GPL. The Licensee can include the Modified or unmodified Software in a code that is subject to the provisions of one of the versions of the GNU GPL, and distribute that entire code under the terms of the same version of the GNU GPL. Article 6 - INTELLECTUAL PROPERTY 6.1 OVER THE INITIAL SOFTWARE The Holder owns the economic rights over the Initial Software. Any or all use of the Initial Software is subject to compliance with the terms and conditions under which the Holder has elected to distribute its work and no one shall be entitled to modify the terms and conditions for the distribution of said Initial Software. The Holder undertakes that the Initial Software will remain ruled at least by this Agreement, for the duration set forth in Article 4.2. 6.2 OVER THE CONTRIBUTIONS The Licensee who develops a Contribution is the owner of the intellectual property rights over this Contribution as defined by applicable law. 6.3 OVER THE EXTERNAL MODULES The Licensee who develops an External Module is the owner of the intellectual property rights over this External Module as defined by applicable law and is free to choose the type of agreement that shall govern its distribution. 6.4 JOINT PROVISIONS The Licensee expressly undertakes: 1. not to remove, or modify, in any manner, the intellectual property notices attached to the Software; 2. to reproduce said notices, in an identical manner, in the copies of the Software modified or not. The Licensee undertakes not to directly or indirectly infringe the intellectual property rights of the Holder and/or Contributors on the Software and to take, where applicable, vis-à-vis its staff, any and all measures required to ensure respect of said intellectual property rights of the Holder and/or Contributors. Article 7 - RELATED SERVICES 7.1 Under no circumstances shall the Agreement oblige the Licensor to provide technical assistance or maintenance services for the Software. However, the Licensor is entitled to offer this type of services. The terms and conditions of such technical assistance, and/or such maintenance, shall be set forth in a separate instrument. Only the Licensor offering said maintenance and/or technical assistance services shall incur liability therefor. 7.2 Similarly, any Licensor is entitled to offer to its licensees, under its sole responsibility, a warranty, that shall only be binding upon itself, for the redistribution of the Software and/or the Modified Software, under terms and conditions that it is free to decide. Said warranty, and the financial terms and conditions of its application, shall be subject of a separate instrument executed between the Licensor and the Licensee. Article 8 - LIABILITY 8.1 Subject to the provisions of Article 8.2, the Licensee shall be entitled to claim compensation for any direct loss it may have suffered from the Software as a result of a fault on the part of the relevant Licensor, subject to providing evidence thereof. 8.2 The Licensor's liability is limited to the commitments made under this Agreement and shall not be incurred as a result of in particular: (i) loss due the Licensee's total or partial failure to fulfill its obligations, (ii) direct or consequential loss that is suffered by the Licensee due to the use or performance of the Software, and (iii) more generally, any consequential loss. In particular the Parties expressly agree that any or all pecuniary or business loss (i.e. loss of data, loss of profits, operating loss, loss of customers or orders, opportunity cost, any disturbance to business activities) or any or all legal proceedings instituted against the Licensee by a third party, shall constitute consequential loss and shall not provide entitlement to any or all compensation from the Licensor. Article 9 - WARRANTY 9.1 The Licensee acknowledges that the scientific and technical state-of-the-art when the Software was distributed did not enable all possible uses to be tested and verified, nor for the presence of possible defects to be detected. In this respect, the Licensee's attention has been drawn to the risks associated with loading, using, modifying and/or developing and reproducing the Software which are reserved for experienced users. The Licensee shall be responsible for verifying, by any or all means, the suitability of the product for its requirements, its good working order, and for ensuring that it shall not cause damage to either persons or properties. 9.2 The Licensor hereby represents, in good faith, that it is entitled to grant all the rights over the Software (including in particular the rights set forth in Article 5). 9.3 The Licensee acknowledges that the Software is supplied "as is" by the Licensor without any other express or tacit warranty, other than that provided for in Article 9.2 and, in particular, without any warranty as to its commercial value, its secured, safe, innovative or relevant nature. Specifically, the Licensor does not warrant that the Software is free from any error, that it will operate without interruption, that it will be compatible with the Licensee's own equipment and software configuration, nor that it will meet the Licensee's requirements. 9.4 The Licensor does not either expressly or tacitly warrant that the Software does not infringe any third party intellectual property right relating to a patent, software or any other property right. Therefore, the Licensor disclaims any and all liability towards the Licensee arising out of any or all proceedings for infringement that may be instituted in respect of the use, modification and redistribution of the Software. Nevertheless, should such proceedings be instituted against the Licensee, the Licensor shall provide it with technical and legal assistance for its defense. Such technical and legal assistance shall be decided on a case-by-case basis between the relevant Licensor and the Licensee pursuant to a memorandum of understanding. The Licensor disclaims any and all liability as regards the Licensee's use of the name of the Software. No warranty is given as regards the existence of prior rights over the name of the Software or as regards the existence of a trademark. Article 10 - TERMINATION 10.1 In the event of a breach by the Licensee of its obligations hereunder, the Licensor may automatically terminate this Agreement thirty (30) days after notice has been sent to the Licensee and has remained ineffective. 10.2 A Licensee whose Agreement is terminated shall no longer be authorized to use, modify or distribute the Software. However, any licenses that it may have granted prior to termination of the Agreement shall remain valid subject to their having been granted in compliance with the terms and conditions hereof. Article 11 - MISCELLANEOUS 11.1 EXCUSABLE EVENTS Neither Party shall be liable for any or all delay, or failure to perform the Agreement, that may be attributable to an event of force majeure, an act of God or an outside cause, such as defective functioning or interruptions of the electricity or telecommunications networks, network paralysis following a virus attack, intervention by government authorities, natural disasters, water damage, earthquakes, fire, explosions, strikes and labor unrest, war, etc. 11.2 Any failure by either Party, on one or more occasions, to invoke one or more of the provisions hereof, shall under no circumstances be interpreted as being a waiver by the interested Party of its right to invoke said provision(s) subsequently. 11.3 The Agreement cancels and replaces any or all previous agreements, whether written or oral, between the Parties and having the same purpose, and constitutes the entirety of the agreement between said Parties concerning said purpose. No supplement or modification to the terms and conditions hereof shall be effective as between the Parties unless it is made in writing and signed by their duly authorized representatives. 11.4 In the event that one or more of the provisions hereof were to conflict with a current or future applicable act or legislative text, said act or legislative text shall prevail, and the Parties shall make the necessary amendments so as to comply with said act or legislative text. All other provisions shall remain effective. Similarly, invalidity of a provision of the Agreement, for any reason whatsoever, shall not cause the Agreement as a whole to be invalid. 11.5 LANGUAGE The Agreement is drafted in both French and English and both versions are deemed authentic. Article 12 - NEW VERSIONS OF THE AGREEMENT 12.1 Any person is authorized to duplicate and distribute copies of this Agreement. 12.2 So as to ensure coherence, the wording of this Agreement is protected and may only be modified by the authors of the License, who reserve the right to periodically publish updates or new versions of the Agreement, each with a separate number. These subsequent versions may address new issues encountered by Free Software. 12.3 Any Software distributed under a given version of the Agreement may only be subsequently distributed under the same version of the Agreement or a subsequent version, subject to the provisions of Article 5.3.4. Article 13 - GOVERNING LAW AND JURISDICTION 13.1 The Agreement is governed by French law. The Parties agree to endeavor to seek an amicable solution to any disagreements or disputes that may arise during the performance of the Agreement. 13.2 Failing an amicable solution within two (2) months as from their occurrence, and unless emergency proceedings are necessary, the disagreements or disputes shall be referred to the Paris Courts having jurisdiction, by the more diligent Party. Version 2.0 dated 2006-09-05. ###################################################################### CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL Avertissement Ce contrat est une licence de logiciel libre issue d'une concertation entre ses auteurs afin que le respect de deux grands principes préside à sa rédaction: * d'une part, le respect des principes de diffusion des logiciels libres: accès au code source, droits étendus conférés aux utilisateurs, * d'autre part, la désignation d'un droit applicable, le droit français, auquel elle est conforme, tant au regard du droit de la responsabilité civile que du droit de la propriété intellectuelle et de la protection qu'il offre aux auteurs et titulaires des droits patrimoniaux sur un logiciel. Les auteurs de la licence CeCILL (pour Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) sont: Commissariat à l'Energie Atomique - CEA, établissement public de recherche à caractère scientifique, technique et industriel, dont le siège est situé 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris. Centre National de la Recherche Scientifique - CNRS, établissement public à caractère scientifique et technologique, dont le siège est situé 3 rue Michel-Ange, 75794 Paris cedex 16. Institut National de Recherche en Informatique et en Automatique - INRIA, établissement public à caractère scientifique et technologique, dont le siège est situé Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex. Préambule Ce contrat est une licence de logiciel libre dont l'objectif est de conférer aux utilisateurs la liberté de modification et de redistribution du logiciel régi par cette licence dans le cadre d'un modèle de diffusion en logiciel libre. L'exercice de ces libertés est assorti de certains devoirs à la charge des utilisateurs afin de préserver ce statut au cours des redistributions ultérieures. L'accessibilité au code source et les droits de copie, de modification et de redistribution qui en découlent ont pour contrepartie de n'offrir aux utilisateurs qu'une garantie limitée et de ne faire peser sur l'auteur du logiciel, le titulaire des droits patrimoniaux et les concédants successifs qu'une responsabilité restreinte. A cet égard l'attention de l'utilisateur est attirée sur les risques associés au chargement, à l'utilisation, à la modification et/ou au développement et à la reproduction du logiciel par l'utilisateur étant donné sa spécificité de logiciel libre, qui peut le rendre complexe à manipuler et qui le réserve donc à des développeurs ou des professionnels avertis possédant des connaissances informatiques approfondies. Les utilisateurs sont donc invités à charger et tester l'adéquation du logiciel à leurs besoins dans des conditions permettant d'assurer la sécurité de leurs systèmes et/ou de leurs données et, plus généralement, à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. Ce contrat peut être reproduit et diffusé librement, sous réserve de le conserver en l'état, sans ajout ni suppression de clauses. Ce contrat est susceptible de s'appliquer à tout logiciel dont le titulaire des droits patrimoniaux décide de soumettre l'exploitation aux dispositions qu'il contient. Article 1 - DEFINITIONS Dans ce contrat, les termes suivants, lorsqu'ils seront écrits avec une lettre capitale, auront la signification suivante: Contrat: désigne le présent contrat de licence, ses éventuelles versions postérieures et annexes. Logiciel: désigne le logiciel sous sa forme de Code Objet et/ou de Code Source et le cas échéant sa documentation, dans leur état au moment de l'acceptation du Contrat par le Licencié. Logiciel Initial: désigne le Logiciel sous sa forme de Code Source et éventuellement de Code Objet et le cas échéant sa documentation, dans leur état au moment de leur première diffusion sous les termes du Contrat. Logiciel Modifié: désigne le Logiciel modifié par au moins une Contribution. Code Source: désigne l'ensemble des instructions et des lignes de programme du Logiciel et auquel l'accès est nécessaire en vue de modifier le Logiciel. Code Objet: désigne les fichiers binaires issus de la compilation du Code Source. Titulaire: désigne le ou les détenteurs des droits patrimoniaux d'auteur sur le Logiciel Initial. Licencié: désigne le ou les utilisateurs du Logiciel ayant accepté le Contrat. Contributeur: désigne le Licencié auteur d'au moins une Contribution. Concédant: désigne le Titulaire ou toute personne physique ou morale distribuant le Logiciel sous le Contrat. Contribution: désigne l'ensemble des modifications, corrections, traductions, adaptations et/ou nouvelles fonctionnalités intégrées dans le Logiciel par tout Contributeur, ainsi que tout Module Interne. Module: désigne un ensemble de fichiers sources y compris leur documentation qui permet de réaliser des fonctionnalités ou services supplémentaires à ceux fournis par le Logiciel. Module Externe: désigne tout Module, non dérivé du Logiciel, tel que ce Module et le Logiciel s'exécutent dans des espaces d'adressage différents, l'un appelant l'autre au moment de leur exécution. Module Interne: désigne tout Module lié au Logiciel de telle sorte qu'ils s'exécutent dans le même espace d'adressage. GNU GPL: désigne la GNU General Public License dans sa version 2 ou toute version ultérieure, telle que publiée par Free Software Foundation Inc. Parties: désigne collectivement le Licencié et le Concédant. Ces termes s'entendent au singulier comme au pluriel. Article 2 - OBJET Le Contrat a pour objet la concession par le Concédant au Licencié d'une licence non exclusive, cessible et mondiale du Logiciel telle que définie ci-après à l'article 5 pour toute la durée de protection des droits portant sur ce Logiciel. Article 3 - ACCEPTATION 3.1 L'acceptation par le Licencié des termes du Contrat est réputée acquise du fait du premier des faits suivants: * (i) le chargement du Logiciel par tout moyen notamment par téléchargement à partir d'un serveur distant ou par chargement à partir d'un support physique; * (ii) le premier exercice par le Licencié de l'un quelconque des droits concédés par le Contrat. 3.2 Un exemplaire du Contrat, contenant notamment un avertissement relatif aux spécificités du Logiciel, à la restriction de garantie et à la limitation à un usage par des utilisateurs expérimentés a été mis à disposition du Licencié préalablement à son acceptation telle que définie à l'article 3.1 ci dessus et le Licencié reconnaît en avoir pris connaissance. Article 4 - ENTREE EN VIGUEUR ET DUREE 4.1 ENTREE EN VIGUEUR Le Contrat entre en vigueur à la date de son acceptation par le Licencié telle que définie en 3.1. 4.2 DUREE Le Contrat produira ses effets pendant toute la durée légale de protection des droits patrimoniaux portant sur le Logiciel. Article 5 - ETENDUE DES DROITS CONCEDES Le Concédant concède au Licencié, qui accepte, les droits suivants sur le Logiciel pour toutes destinations et pour la durée du Contrat dans les conditions ci-après détaillées. Par ailleurs, si le Concédant détient ou venait à détenir un ou plusieurs brevets d'invention protégeant tout ou partie des fonctionnalités du Logiciel ou de ses composants, il s'engage à ne pas opposer les éventuels droits conférés par ces brevets aux Licenciés successifs qui utiliseraient, exploiteraient ou modifieraient le Logiciel. En cas de cession de ces brevets, le Concédant s'engage à faire reprendre les obligations du présent alinéa aux cessionnaires. 5.1 DROIT D'UTILISATION Le Licencié est autorisé à utiliser le Logiciel, sans restriction quant aux domaines d'application, étant ci-après précisé que cela comporte: 1. la reproduction permanente ou provisoire du Logiciel en tout ou partie par tout moyen et sous toute forme. 2. le chargement, l'affichage, l'exécution, ou le stockage du Logiciel sur tout support. 3. la possibilité d'en observer, d'en étudier, ou d'en tester le fonctionnement afin de déterminer les idées et principes qui sont à la base de n'importe quel élément de ce Logiciel; et ceci, lorsque le Licencié effectue toute opération de chargement, d'affichage, d'exécution, de transmission ou de stockage du Logiciel qu'il est en droit d'effectuer en vertu du Contrat. 5.2 DROIT D'APPORTER DES CONTRIBUTIONS Le droit d'apporter des Contributions comporte le droit de traduire, d'adapter, d'arranger ou d'apporter toute autre modification au Logiciel et le droit de reproduire le logiciel en résultant. Le Licencié est autorisé à apporter toute Contribution au Logiciel sous réserve de mentionner, de façon explicite, son nom en tant qu'auteur de cette Contribution et la date de création de celle-ci. 5.3 DROIT DE DISTRIBUTION Le droit de distribution comporte notamment le droit de diffuser, de transmettre et de communiquer le Logiciel au public sur tout support et par tout moyen ainsi que le droit de mettre sur le marché à titre onéreux ou gratuit, un ou des exemplaires du Logiciel par tout procédé. Le Licencié est autorisé à distribuer des copies du Logiciel, modifié ou non, à des tiers dans les conditions ci-après détaillées. 5.3.1 DISTRIBUTION DU LOGICIEL SANS MODIFICATION Le Licencié est autorisé à distribuer des copies conformes du Logiciel, sous forme de Code Source ou de Code Objet, à condition que cette distribution respecte les dispositions du Contrat dans leur totalité et soit accompagnée: 1. d'un exemplaire du Contrat, 2. d'un avertissement relatif à la restriction de garantie et de responsabilité du Concédant telle que prévue aux articles 8 et 9, et que, dans le cas où seul le Code Objet du Logiciel est redistribué, le Licencié permette aux futurs Licenciés d'accéder facilement au Code Source complet du Logiciel en indiquant les modalités d'accès, étant entendu que le coût additionnel d'acquisition du Code Source ne devra pas excéder le simple coût de transfert des données. 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE Lorsque le Licencié apporte une Contribution au Logiciel, les conditions de distribution du Logiciel Modifié en résultant sont alors soumises à l'intégralité des dispositions du Contrat. Le Licencié est autorisé à distribuer le Logiciel Modifié, sous forme de code source ou de code objet, à condition que cette distribution respecte les dispositions du Contrat dans leur totalité et soit accompagnée: 1. d'un exemplaire du Contrat, 2. d'un avertissement relatif à la restriction de garantie et de responsabilité du Concédant telle que prévue aux articles 8 et 9, et que, dans le cas où seul le code objet du Logiciel Modifié est redistribué, le Licencié permette aux futurs Licenciés d'accéder facilement au code source complet du Logiciel Modifié en indiquant les modalités d'accès, étant entendu que le coût additionnel d'acquisition du code source ne devra pas excéder le simple coût de transfert des données. 5.3.3 DISTRIBUTION DES MODULES EXTERNES Lorsque le Licencié a développé un Module Externe les conditions du Contrat ne s'appliquent pas à ce Module Externe, qui peut être distribué sous un contrat de licence différent. 5.3.4 COMPATIBILITE AVEC LA LICENCE GNU GPL Le Licencié peut inclure un code soumis aux dispositions d'une des versions de la licence GNU GPL dans le Logiciel modifié ou non et distribuer l'ensemble sous les conditions de la même version de la licence GNU GPL. Le Licencié peut inclure le Logiciel modifié ou non dans un code soumis aux dispositions d'une des versions de la licence GNU GPL et distribuer l'ensemble sous les conditions de la même version de la licence GNU GPL. Article 6 - PROPRIETE INTELLECTUELLE 6.1 SUR LE LOGICIEL INITIAL Le Titulaire est détenteur des droits patrimoniaux sur le Logiciel Initial. Toute utilisation du Logiciel Initial est soumise au respect des conditions dans lesquelles le Titulaire a choisi de diffuser son oeuvre et nul autre n'a la faculté de modifier les conditions de diffusion de ce Logiciel Initial. Le Titulaire s'engage à ce que le Logiciel Initial reste au moins régi par le Contrat et ce, pour la durée visée à l'article 4.2. 6.2 SUR LES CONTRIBUTIONS Le Licencié qui a développé une Contribution est titulaire sur celle-ci des droits de propriété intellectuelle dans les conditions définies par la législation applicable. 6.3 SUR LES MODULES EXTERNES Le Licencié qui a développé un Module Externe est titulaire sur celui-ci des droits de propriété intellectuelle dans les conditions définies par la législation applicable et reste libre du choix du contrat régissant sa diffusion. 6.4 DISPOSITIONS COMMUNES Le Licencié s'engage expressément: 1. à ne pas supprimer ou modifier de quelque manière que ce soit les mentions de propriété intellectuelle apposées sur le Logiciel; 2. à reproduire à l'identique lesdites mentions de propriété intellectuelle sur les copies du Logiciel modifié ou non. Le Licencié s'engage à ne pas porter atteinte, directement ou indirectement, aux droits de propriété intellectuelle du Titulaire et/ou des Contributeurs sur le Logiciel et à prendre, le cas échéant, à l'égard de son personnel toutes les mesures nécessaires pour assurer le respect des dits droits de propriété intellectuelle du Titulaire et/ou des Contributeurs. Article 7 - SERVICES ASSOCIES 7.1 Le Contrat n'oblige en aucun cas le Concédant à la réalisation de prestations d'assistance technique ou de maintenance du Logiciel. Cependant le Concédant reste libre de proposer ce type de services. Les termes et conditions d'une telle assistance technique et/ou d'une telle maintenance seront alors déterminés dans un acte séparé. Ces actes de maintenance et/ou assistance technique n'engageront que la seule responsabilité du Concédant qui les propose. 7.2 De même, tout Concédant est libre de proposer, sous sa seule responsabilité, à ses licenciés une garantie, qui n'engagera que lui, lors de la redistribution du Logiciel et/ou du Logiciel Modifié et ce, dans les conditions qu'il souhaite. Cette garantie et les modalités financières de son application feront l'objet d'un acte séparé entre le Concédant et le Licencié. Article 8 - RESPONSABILITE 8.1 Sous réserve des dispositions de l'article 8.2, le Licencié a la faculté, sous réserve de prouver la faute du Concédant concerné, de solliciter la réparation du préjudice direct qu'il subirait du fait du Logiciel et dont il apportera la preuve. 8.2 La responsabilité du Concédant est limitée aux engagements pris en application du Contrat et ne saurait être engagée en raison notamment: (i) des dommages dus à l'inexécution, totale ou partielle, de ses obligations par le Licencié, (ii) des dommages directs ou indirects découlant de l'utilisation ou des performances du Logiciel subis par le Licencié et (iii) plus généralement d'un quelconque dommage indirect. En particulier, les Parties conviennent expressément que tout préjudice financier ou commercial (par exemple perte de données, perte de bénéfices, perte d'exploitation, perte de clientèle ou de commandes, manque à gagner, trouble commercial quelconque) ou toute action dirigée contre le Licencié par un tiers, constitue un dommage indirect et n'ouvre pas droit à réparation par le Concédant. Article 9 - GARANTIE 9.1 Le Licencié reconnaît que l'état actuel des connaissances scientifiques et techniques au moment de la mise en circulation du Logiciel ne permet pas d'en tester et d'en vérifier toutes les utilisations ni de détecter l'existence d'éventuels défauts. L'attention du Licencié a été attirée sur ce point sur les risques associés au chargement, à l'utilisation, la modification et/ou au développement et à la reproduction du Logiciel qui sont réservés à des utilisateurs avertis. Il relève de la responsabilité du Licencié de contrôler, par tous moyens, l'adéquation du produit à ses besoins, son bon fonctionnement et de s'assurer qu'il ne causera pas de dommages aux personnes et aux biens. 9.2 Le Concédant déclare de bonne foi être en droit de concéder l'ensemble des droits attachés au Logiciel (comprenant notamment les droits visés à l'article 5). 9.3 Le Licencié reconnaît que le Logiciel est fourni "en l'état" par le Concédant sans autre garantie, expresse ou tacite, que celle prévue à l'article 9.2 et notamment sans aucune garantie sur sa valeur commerciale, son caractère sécurisé, innovant ou pertinent. En particulier, le Concédant ne garantit pas que le Logiciel est exempt d'erreur, qu'il fonctionnera sans interruption, qu'il sera compatible avec l'équipement du Licencié et sa configuration logicielle ni qu'il remplira les besoins du Licencié. 9.4 Le Concédant ne garantit pas, de manière expresse ou tacite, que le Logiciel ne porte pas atteinte à un quelconque droit de propriété intellectuelle d'un tiers portant sur un brevet, un logiciel ou sur tout autre droit de propriété. Ainsi, le Concédant exclut toute garantie au profit du Licencié contre les actions en contrefaçon qui pourraient être diligentées au titre de l'utilisation, de la modification, et de la redistribution du Logiciel. Néanmoins, si de telles actions sont exercées contre le Licencié, le Concédant lui apportera son aide technique et juridique pour sa défense. Cette aide technique et juridique est déterminée au cas par cas entre le Concédant concerné et le Licencié dans le cadre d'un protocole d'accord. Le Concédant dégage toute responsabilité quant à l'utilisation de la dénomination du Logiciel par le Licencié. Aucune garantie n'est apportée quant à l'existence de droits antérieurs sur le nom du Logiciel et sur l'existence d'une marque. Article 10 - RESILIATION 10.1 En cas de manquement par le Licencié aux obligations mises à sa charge par le Contrat, le Concédant pourra résilier de plein droit le Contrat trente (30) jours après notification adressée au Licencié et restée sans effet. 10.2 Le Licencié dont le Contrat est résilié n'est plus autorisé à utiliser, modifier ou distribuer le Logiciel. Cependant, toutes les licences qu'il aura concédées antérieurement à la résiliation du Contrat resteront valides sous réserve qu'elles aient été effectuées en conformité avec le Contrat. Article 11 - DISPOSITIONS DIVERSES 11.1 CAUSE EXTERIEURE Aucune des Parties ne sera responsable d'un retard ou d'une défaillance d'exécution du Contrat qui serait dû à un cas de force majeure, un cas fortuit ou une cause extérieure, telle que, notamment, le mauvais fonctionnement ou les interruptions du réseau électrique ou de télécommunication, la paralysie du réseau liée à une attaque informatique, l'intervention des autorités gouvernementales, les catastrophes naturelles, les dégâts des eaux, les tremblements de terre, le feu, les explosions, les grèves et les conflits sociaux, l'état de guerre... 11.2 Le fait, par l'une ou l'autre des Parties, d'omettre en une ou plusieurs occasions de se prévaloir d'une ou plusieurs dispositions du Contrat, ne pourra en aucun cas impliquer renonciation par la Partie intéressée à s'en prévaloir ultérieurement. 11.3 Le Contrat annule et remplace toute convention antérieure, écrite ou orale, entre les Parties sur le même objet et constitue l'accord entier entre les Parties sur cet objet. Aucune addition ou modification aux termes du Contrat n'aura d'effet à l'égard des Parties à moins d'être faite par écrit et signée par leurs représentants dûment habilités. 11.4 Dans l'hypothèse où une ou plusieurs des dispositions du Contrat s'avèrerait contraire à une loi ou à un texte applicable, existants ou futurs, cette loi ou ce texte prévaudrait, et les Parties feraient les amendements nécessaires pour se conformer à cette loi ou à ce texte. Toutes les autres dispositions resteront en vigueur. De même, la nullité, pour quelque raison que ce soit, d'une des dispositions du Contrat ne saurait entraîner la nullité de l'ensemble du Contrat. 11.5 LANGUE Le Contrat est rédigé en langue française et en langue anglaise, ces deux versions faisant également foi. Article 12 - NOUVELLES VERSIONS DU CONTRAT 12.1 Toute personne est autorisée à copier et distribuer des copies de ce Contrat. 12.2 Afin d'en préserver la cohérence, le texte du Contrat est protégé et ne peut être modifié que par les auteurs de la licence, lesquels se réservent le droit de publier périodiquement des mises à jour ou de nouvelles versions du Contrat, qui posséderont chacune un numéro distinct. Ces versions ultérieures seront susceptibles de prendre en compte de nouvelles problématiques rencontrées par les logiciels libres. 12.3 Tout Logiciel diffusé sous une version donnée du Contrat ne pourra faire l'objet d'une diffusion ultérieure que sous la même version du Contrat ou une version postérieure, sous réserve des dispositions de l'article 5.3.4. Article 13 - LOI APPLICABLE ET COMPETENCE TERRITORIALE 13.1 Le Contrat est régi par la loi française. Les Parties conviennent de tenter de régler à l'amiable les différends ou litiges qui viendraient à se produire par suite ou à l'occasion du Contrat. 13.2 A défaut d'accord amiable dans un délai de deux (2) mois à compter de leur survenance et sauf situation relevant d'une procédure d'urgence, les différends ou litiges seront portés par la Partie la plus diligente devant les Tribunaux compétents de Paris. Version 2.0 du 2006-09-05. kissplice-2.4.0-p1/debruijn-v4/CMakeLists.txt000644 000765 000024 00000003540 12676004527 021024 0ustar00ishistaff000000 000000 # Manage the KMERS_OVER_32 option for graph construction if (KMERS_OVER_32) add_definitions(-Dkmer_type=__uint128_t) endif (KMERS_OVER_32) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) include_directories(${ZLIB_INCLUDE_DIRS}) add_executable(ks_debruijn4 main.cpp minia/Pool.cpp minia/Bank.cpp minia/Bloom.cpp minia/Hash16.cpp minia/Kmer.cpp minia/Set.cpp minia/Utils.cpp minia/SortingCount.cpp minia/LinearCounter.cpp minia/Debloom.cpp minia/Traversal.cpp minia/Terminator.cpp minia/OAHash.cpp minia/GraphOutput.cpp minia/Bank.h minia/Debloom.h minia/Hash16.h minia/LinearCounter.h minia/Pool.h minia/SortingCount.h minia/Traversal.h minia/lut.h minia/Bloom.h minia/GraphOutput.h minia/Kmer.h minia/OAHash.h minia/Set.h minia/Terminator.h minia/Utils.h) # Declare use of c++11 features # TODO: This conditional should be removed as soon as cmake 2.8.12 or greater # becomes acceptable as a requirement since the solution using target_compile_options # (introduced in 2.8.12) is a lot cleaner. IF ((${CMAKE_VERSION} VERSION_LESS 2.8.12) ) SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++11") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++11") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") SET(CMAKE_C_FLAGS_DISTRIBUTION "${CMAKE_C_FLAGS_DISTRIBUTION} -std=c++11") SET(CMAKE_CXX_FLAGS_DISTRIBUTION "${CMAKE_CXX_FLAGS_DISTRIBUTION} -std=c++11") SET(CMAKE_C_FLAGS_PROFILING "${CMAKE_C_FLAGS_PROFILING} -std=c++11") SET(CMAKE_CXX_FLAGS_PROFILING "${CMAKE_CXX_FLAGS_PROFILING} -std=c++11") ELSE () target_compile_options(ks_debruijn4 PRIVATE "-std=c++11") ENDIF () include_directories( ${ZLIB_INCLUDE_DIR} ) target_link_libraries(ks_debruijn4 ${ZLIB_LIBRARY}) install(TARGETS ks_debruijn4 DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) kissplice-2.4.0-p1/debruijn-v4/main.cpp000644 000765 000024 00000024326 12676004527 017721 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include // for mkdir #include #include #include // for max/min #include // for sorting_kmers #include #define NNKS 4 // default minimal abundance for solidity int max_memory; // the most memory one should alloc at any time, in MB int order=0; // in minigraph, don't change it, it should be 0 #include "minia/Bank.h" #include "minia/Hash16.h" #include "minia/Set.h" #include "minia/Pool.h" #include "minia/Bloom.h" #include "minia/Debloom.h" #include "minia/Utils.h" #include "minia/SortingCount.h" #include "minia/Terminator.h" #include "minia/Kmer.h" #include "minia/GraphOutput.h" #include "minia/rvalues.h" // for 4bloom int64_t genome_size; Bloom * bloo1; FILE *linear_seqs_file; string linear_seqs_name; const string linear_seqs_suffix = ".linearSeqs"; int graph_format = 0; // 0 = dot, 1 = kissplice, 2 = xgmml, 3 = json void construct_linear_seqs() { kmer_type branching_kmer, kmer; char kmer_seq[sizeKmer+1]; BinaryBank *SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer_type),0); BranchingTerminator *terminator; terminator = new BranchingTerminator(SolidKmers,genome_size, bloo1,false_positives); /* RandomBranchingTraversal *traversal = new RandomBranchingTraversal(bloo1,false_positives,terminator); printf("\n\nWARNING! random traversal\n\n");*/ SimplePathsTraversal *traversal = new SimplePathsTraversal(bloo1,false_positives,terminator); //MonumentTraversal *traversal = new MonumentTraversal(bloo1,false_positives,terminator); long long nbNodes = 0; long long totalnt=0; long long mlenleft=0,mlenright=0; int64_t NbBranchingKmer=0; long long len_left = 0; long long len_right = 0; long long contig_len =0; long long maxlen=10000000; char *left_traversal = (char *) malloc(maxlen/2*sizeof(char)); char *right_traversal = (char *) malloc(maxlen/2*sizeof(char)); char *node = (char *) malloc(maxlen*sizeof(char)); linear_seqs_file = fopen((char * )linear_seqs_name.c_str(),"w"); printf("starting nodes construction\n"); STARTWALL(nodes); while (terminator->next(&branching_kmer)) { while (traversal->get_new_starting_node(branching_kmer,kmer)) { code2seq(kmer,kmer_seq); // convert starting kmer to nucleotide seq //printf("new starting node: %s\n",kmer_seq); // right extension len_right = traversal->traverse(kmer,right_traversal,0); mlenright= max(len_right,mlenright); // left extension, is equivalent to right extension of the revcomp len_left = traversal->traverse(kmer,left_traversal,1); mlenleft= max(len_left,mlenleft); // form the node revcomp_sequence(left_traversal,len_left); strcpy(node,left_traversal); // node = revcomp(left_traversal) strcat(node,kmer_seq);// + starting_kmer strcat(node,right_traversal);// + right_traversal int node_len=len_left+len_right+sizeKmer; // save the node fprintf(linear_seqs_file,">%lli__len__%i \n",nbNodes,node_len); fprintf(linear_seqs_file,"%s\n",node); nbNodes++; totalnt+=node_len; } NbBranchingKmer++; if ((NbBranchingKmer%300)==0) fprintf (stderr,"%cLooping through branching kmer n° %lld / %lld ",13,NbBranchingKmer,terminator->nb_branching_kmers); } fprintf(stderr, "\n"); STOPWALL(nodes,"nodes construction"); // TODO: do another pass of kmers to detect what we missed: // - perfectly circular regions without any branching (btw, stop as soon as a branching kmer is detected..) // - that's it delete terminator; fclose(linear_seqs_file); free(left_traversal); free(right_traversal); free(node); SolidKmers->close(); } int main(int argc, char *argv[]) { if(argc < 6) { fprintf (stderr,"usage:\n"); fprintf (stderr," %s reads_file kmer_size min_abundance estimated_genome_size prefix [--json] [--kissplice] [--4bloom]\n",argv[0]); fprintf (stderr,"hints:\n reads_file is either a fasta/fastq/fasta.gz/fastq.gz or a text file containing a reads file at each line\n min_abundance ~ 3\n estimated_genome_size is in bp, does not need to be accurate, only controls memory usage\n prefix is any name you want\n with the --4bloom option the representation with 4 Bloom filters is used instead of the standard one\n"); return 0; } int FOUR_BLOOM_VERSION = 0; // shortcuts to go directly to assembly using serialized bloom and serialized hash int START_FROM_SOLID_KMERS=0; // if = 0, construct the fasta file of solid kmers, if = 1, start directly from that file int LOAD_FALSE_POSITIVE_KMERS=0; // if = 0, construct the fasta file of false positive kmers (debloom), if = 1, load that file into the hashtable int NO_FALSE_POSITIVES_AT_ALL=0; // if = 0, normal behavior, if = 1, don't load false positives (will be a probabilistic de bruijn graph) for (int n_a = 6; n_a < argc ; n_a++) { if (strcmp(argv[n_a],"--4bloom") == 0) FOUR_BLOOM_VERSION = 1; if (strcmp(argv[n_a],"--dont-count")==0) START_FROM_SOLID_KMERS = 1; if (strcmp(argv[n_a],"--dont-debloom")==0) LOAD_FALSE_POSITIVE_KMERS = 1; if (strcmp(argv[n_a],"--just-graph")==0) { START_FROM_SOLID_KMERS = 1; LOAD_FALSE_POSITIVE_KMERS = 1; } if (strcmp(argv[n_a],"--json")==0) graph_format = 3; if (strcmp(argv[n_a],"--kissplice")==0) graph_format = 1; } // kmer size sizeKmer=27; // let's make it even for now, because i havnt thought of how to handle palindromes (dont want to stop on them) if(argc >= 3) { sizeKmer = atoi(argv[2]); if (sizeKmer%2==0) { sizeKmer-=1; printf("Need odd kmer size to avoid palindromes. I've set kmer size to %d.\n",sizeKmer); } if (sizeKmer>=(sizeof(kmer_type)*4)) { printf("Max kmer size on this compiled version is %d\n",sizeof(kmer_type)*4-2); exit(1); } } kmerMask=(((kmer_type)1)<<(sizeKmer*2))-1; double lg2 = log(2); if (!FOUR_BLOOM_VERSION) NBITS_PER_KMER = log(16*sizeKmer*(lg2*lg2))/(lg2*lg2); // needed to process argv[5] else NBITS_PER_KMER = rvalues[sizeKmer][1]; // solidity nks =NNKS; if(argc >= 4) { nks = atoi(argv[3]); } if(argc >= 5) { genome_size = atoll(argv[4]); int estimated_bloom_size = max( (int)ceilf(log2f(genome_size * NBITS_PER_KMER )), 1); uint64_t estimated_nb_FP = (uint64_t)(genome_size * 4 * powf(0.6,11)); // just indicative max_memory = max( (1LL << estimated_bloom_size)/8LL /1024LL/1024LL, 1LL ); printf("estimated values: nbits Bloom %i, nb FP %lld, max memory %i MB\n",estimated_bloom_size,estimated_nb_FP,max_memory); } // output prefix if(argc >= 6) { strcpy(prefix,argv[5]); } fprintf (stderr,"taille cell %lu \n", sizeof(cell)); STARTWALL(0); Bank *Reads = new Bank(argv[1]); // count kmers, write solid kmers + count to disk if (!START_FROM_SOLID_KMERS) { int max_disk_space = 0; // let dsk decide int verbose = 0; sorting_count(Reads, prefix, max_memory, max_disk_space, true, verbose); // convert [solid kmers with count] to [solid kmers without count] string solid_kmers_with_count_filename = return_file_name(solid_kmers_file); solid_kmers_with_count_filename += "_with_count"; rename(return_file_name(solid_kmers_file),solid_kmers_with_count_filename.c_str()); BinaryBank *SolidKmersWithCount = new BinaryBank((char *)solid_kmers_with_count_filename.c_str(),sizeof(kmer_type),false); BinaryBank * SolidKmersWithoutCount = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer_type),true); uint64_t osef; uint_abundance_t abundance; kmer_type kmer; SolidKmersWithCount->read(&osef, 8); //read the header while (SolidKmersWithCount->read_element(&kmer)) { SolidKmersWithoutCount->write_element(&kmer); SolidKmersWithCount->read(&abundance, sizeof(abundance)); } SolidKmersWithCount->close(); SolidKmersWithoutCount->close(); } delete Reads; STARTWALL(buildDBG); // debloom, write false positives to disk, insert them into false_positives if (! LOAD_FALSE_POSITIVE_KMERS) { debloom(order, max_memory); } bloo1 = bloom_create_bloo1((BloomCpt *)NULL); if (! NO_FALSE_POSITIVES_AT_ALL) { // load false positives from disk into false_positives if (!FOUR_BLOOM_VERSION) false_positives = load_false_positives(); else false_positives = load_false_positives_cascading4(); } else { // titus mode: no FP's dummy_false_positives(); } STOPWALL(buildDBG, "build DBG"); //////------------------------------------------------------------------------------------------- fprintf (stderr,"______________________________________________________ \n"); fprintf (stderr,"_______________________________________ minigraph_____ \n"); fprintf (stderr,"______________________________________________________ \n\n"); //////------------------------------------------------------------------------------------------- linear_seqs_name=(prefix+linear_seqs_suffix); construct_linear_seqs(); delete bloo1; // start a graph GraphOutput graph = GraphOutput(prefix,graph_format); graph.load_nodes_extremities(linear_seqs_name); printf("starting edges construction\n"); STARTWALL(edges); graph.construct_graph(linear_seqs_name); STOPWALL(edges,"edges construction"); graph.close(); //////------------------------------------------------------------------------------------------- STOPWALL(0,"Total"); return 0; } kissplice-2.4.0-p1/debruijn-v4/minia/Bank.cpp000644 000765 000024 00000076331 12676004527 020750 0ustar00ishistaff000000 000000 // // Bank.cpp // // Created by Guillaume Rizk on 28/11/11. // //TEST #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include // for log2f #include "Bank.h" #include "Kmer.h" // Bank (almost) doesn't need Kmer.h, but KmersBuffer certainly does #include "lut.h" #include using namespace std; off_t fsize(const char *filename) { struct stat st; if (stat(filename, &st) == 0) return st.st_size; return -1; } // just a macro to open file indexed by i void Bank::open_stream(int i) { buffered_file[i]->stream = gzopen(buffered_file[i]->fname,"r"); if (buffered_file[i]->stream == NULL) { printf("error opening file: %s\n",buffered_file[i]->fname); exit(1); } } // and close it void Bank::close_stream(int i) { gzclose(buffered_file[i]->stream); buffered_file[i]->stream = NULL; } // the following functions are adapted from kseq.h by Heng Li (https://github.com/attractivechaos/klib) inline bool rebuffer(buffered_file_t *bf) { if (bf->eof) return false; bf->buffer_start = 0; bf->buffer_end = gzread(bf->stream, bf->buffer, BUFFER_SIZE); if (bf->buffer_end < BUFFER_SIZE) bf->eof = 1; if (bf->buffer_end == 0) return false; return true; } inline signed char buffered_getc(buffered_file_t *bf) { if (bf->buffer_start >= bf->buffer_end) if (! rebuffer(bf)) return -1; return (signed char) ( bf->buffer[bf->buffer_start++] ); } #define nearest_power_of_2(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) inline signed int Bank::buffered_gets(buffered_file_t *bf, variable_string_t *s, char *dret, bool append, bool allow_spaces) { if (dret) *dret = 0; if (!append) s->length = 0; if (bf->buffer_start >= bf->buffer_end && bf->eof) return -1; while (1) { int i; if (bf->buffer_start >= bf->buffer_end) if (! rebuffer(bf)) break; if (allow_spaces) { for (i = bf->buffer_start; i < bf->buffer_end ; i++) if (bf->buffer[i] == '\n') break; } else { for (i = bf->buffer_start; i < bf->buffer_end ; i++) // isspace() answers yes for ' ', \t, \n, \v, \f, \r if (isspace(bf->buffer[i])) break; } if (s->max - s->length < (i - bf->buffer_start + 1)) { s->max = s->length + (i - bf->buffer_start + 1); nearest_power_of_2(s->max); s->string = (char*)realloc(s->string,s->max); } memcpy(s->string + s->length, bf->buffer + bf->buffer_start, i - bf->buffer_start); s->length += i - bf->buffer_start; bf->buffer_start = i + 1; if (i < bf->buffer_end) { if (dret) *dret = bf->buffer[i]; break; } } if (s->string == NULL) { s->max = 256; s->string = (char*)calloc(256,1); } else if ( allow_spaces && s->length > 1 && s->string[s->length-1] == '\r') s->length--; s->string[s->length]= '\0'; return s->length; } void Bank::rewind_all() { for (int i=0; istream != NULL) { gzclose(buffered_file[i]->stream); buffered_file[i]->stream = NULL; } buffered_file[i]->last_char = buffered_file[i]->eof = buffered_file[i]->buffer_start = buffered_file[i]->buffer_end = 0; } index_file = 0; open_stream(index_file); } // THIS READS FASTQ or FASTA, compressed with gzip or not // no limit on read length, allows multi-line reads // returns true if a read was successfuly read // false if end of file // adapted from kseq.h by Heng Li (https://github.com/attractivechaos/klib) bool Bank::get_next_seq_from_file(char **nseq, char **cheader, int *len, int *hlen, int file_id) { signed char c; buffered_file_t *bf = buffered_file[file_id]; if (bf->last_char == 0) { while ( (c = buffered_getc(bf)) != -1 && c != '>' && c != '@'); // go to next header if (c == -1) return false; // eof bf->last_char = c; } read->length = dummy->length = 0; if (buffered_gets(bf, header, (char *)&c, false, false) < 0) //ici return false; // eof if (c != '\n') buffered_gets(bf, dummy, NULL, true, true); // read header //dummy instead of header to stop before first space if (read->string == NULL) { read->max = 256; read->string = (char*) malloc(read->max); } while ( (c = buffered_getc(bf)) != -1 && c != '>' && c != '+' && c != '@') { if (c == '\n') continue; // empty line read->string[read->length++] = c; buffered_gets(bf, read, NULL, true, true); } if (c == '>' || c == '@') bf->last_char = c; if (read->length + 1 >= read->max) { read->max = read->length + 2; nearest_power_of_2(read->max); read->string = (char*) realloc(read->string, read->max); } read->string[read->length] = '\0'; if (c == '+') // fastq { if (dummy->max < read->max) // resize quality to match read length { dummy->max = read->max; dummy->string = (char*)realloc(dummy->string, dummy->max); } while ( (c = buffered_getc(bf)) != -1 && c != '\n'); // read rest of quality comment while (buffered_gets(bf, dummy, NULL, true, true) >= 0 && dummy->length < read->length); // read rest of quality bf->last_char = 0; } *len = read->length; *nseq = read->string; if (cheader && hlen) { *cheader = header->string; *hlen = header->length; } return true; } // wrapper bool Bank::get_next_seq_from_file(char **nseq, int *len, int file_id) { return get_next_seq_from_file(nseq,NULL,len,NULL,file_id); } //wrapper with notif when changing file bool Bank::get_next_seq(char **nseq, char **cheader, int *len, int *hlen, int * id_file) { * id_file = index_file ; bool success = get_next_seq_from_file(nseq,cheader,len,hlen,index_file); if (success) return true; // cycle to next file if possible if ( index_file < nb_files-1 ) { close_stream(index_file); index_file++; open_stream(index_file); return get_next_seq(nseq,cheader, len,hlen, id_file); } return false; } // wrapper bool Bank::get_next_seq(char **nseq, char **cheader, int *len, int *hlen) { bool success = get_next_seq_from_file(nseq,cheader,len,hlen,index_file); if (success) return true; // cycle to next file if possible if ( index_file < nb_files-1 ) { close_stream(index_file); index_file++; open_stream(index_file); return get_next_seq(nseq,cheader, len,hlen); } return false; } // wrapper bool Bank::get_next_seq(char **nseq, int *len) { return get_next_seq(nseq,NULL,len,NULL); } // wrapper bool Bank::get_next_seq(char **nseq, int *len, int * id_file) { return get_next_seq(nseq,NULL,len,NULL,id_file); } // had to move the Bank(x,x) constructor to an init() to avoid calling a constructor inside the Bank(x) constructor void Bank::init(char **fname, int nb_files_) { int64_t i; nb_files = nb_files_; filesizes = 0; // open the reads file, don't know if it is a fasta/q file or a list of file names yet gzFile tempfile = gzopen(fname[0],"r"); if (tempfile == NULL) { char *buffer = (char*)malloc(BUFSIZ); strerror_r( errno, buffer, BUFSIZ ); // get string message from errno printf("error during fopen, filename: %s (buffer: %p) \n", fname[0], buffer); free(buffer); exit(1); } char deb=(char)gzgetc(tempfile); char **nfname;// [MAX_NB_FILES][TAILLE_NOM]; nfname = (char**) malloc(sizeof(char*)*MAX_NB_FILES); for(int jj=0; jj' || deb=='@' || deb==EOF) { // file is a fasta/q file gzclose(tempfile); } else // file contains a list of file names { char* ret; gzungetc(deb,tempfile); printf("File %s starts with character \"%c\", hence is interpreted as a list of file names\n",fname[0],deb ); int ii; // get the filenames for (ii=0; iibuffer = (unsigned char*) malloc(BUFFER_SIZE); buffered_file[i]->fname = strdup(fname[i]); } // estimate total size of files for (i=0; iestimated_filesize = estimated_filesize; filesizes += estimated_filesize; } rewind_all(); // initialize the get_next_seq iterator to the first file // init read and dummy (for readname and quality) read = (variable_string_t*) calloc(1,sizeof(variable_string_t)); dummy = (variable_string_t*) calloc(1,sizeof(variable_string_t)); header = (variable_string_t*) calloc(1,sizeof(variable_string_t)); for(int jj=0; jjstring) free(to_free[i]->string); free(to_free[i]); } } for (int i=0; ibuffer); free(buffered_file[i]); } } void Bank::close() { for (int i=0; istream); } // estimate the volume of all redundant kmers in the reads, if they were to be stored in 2bits // from the first 100k reads of each file uint64_t Bank::estimate_kmers_volume(int k) { char * rseq; int readlen; int kmer_nbits = sizeof(kmer_type)*8; rewind_all(); uint64_t total_volume = 0; while ( index_file < nb_files ) { open_stream(index_file); int NbRead = 0; uint64_t volume_for_file = 0; while (get_next_seq_from_file(&rseq,NULL,&readlen,NULL,index_file)) { if (readlen >= k) volume_for_file += (readlen-k+1) * (uint64_t) kmer_nbits; if (NbRead++ == 100000) // somehow less than 100000 is bad for our ion torrent tag1.fasta file break; } if ( gztell(buffered_file[index_file]->stream) != 0) // would be empty file { volume_for_file = (uint64_t) ( ( (float) volume_for_file ) * ( ( (float)(buffered_file[index_file]->estimated_filesize)) / ((float) gztell(buffered_file[index_file]->stream)) ) ); total_volume += volume_for_file; } close_stream(index_file); index_file++; } total_volume = total_volume / 1024 /1024 /8; // put it in MB if (total_volume == 0) // tiny files fix total_volume = 1; rewind_all(); return total_volume; } // estimate the number of reads uint64_t Bank::estimate_nb_reads() { char * rseq; int readlen; int NbRead = 0; rewind_all(); uint64_t volume = 0; while (get_next_seq(&rseq,&readlen)) { volume += 1; if (NbRead++ == 1000) break; } if ( gztell(buffered_file[index_file]->stream) == 0) // empty file return 1; volume = (volume * filesizes) / gztell(buffered_file[index_file]->stream); // linear extrapolation from the first 1k reads rewind_all(); return volume; } // estimate maximum read length // from the first 10000 reads of each file int Bank::estimate_max_readlen() { char * rseq; int readlen; rewind_all(); int max_readlen = 0; uint64_t volume = 0; while ( index_file < nb_files ) { open_stream(index_file); int NbRead = 0; while (get_next_seq_from_file(&rseq,NULL,&readlen,NULL,index_file)) { max_readlen = max(readlen, max_readlen); if (NbRead++ == 10000) break; } close_stream(index_file); index_file++; } rewind_all(); return max_readlen; } void Bank::save_position() { restore_index_file = index_file; restore_pos = gztell(buffered_file[index_file]->stream) - buffered_file[index_file]->buffer_end + buffered_file[index_file]->buffer_start; } void Bank::load_position() { close_stream(index_file); index_file = restore_index_file; open_stream(index_file); gzseek(buffered_file[index_file]->stream, restore_pos, SEEK_SET); buffered_file[index_file]->eof = false; rebuffer(buffered_file[index_file]); } // BinaryBank: a binary file containing kmers BinaryBank::BinaryBank(char *given_filename, int given_sizeElement, bool write) : sizeElement(given_sizeElement) { strcpy(filename,given_filename); open(write); buffer_size_nelem= (WRITE_BUFFER/given_sizeElement); buffer = (void *) malloc(given_sizeElement * buffer_size_nelem); cpt_buffer=0; } BinaryBankConcurrent::BinaryBankConcurrent(char *given_filename, int given_sizeElement, bool write, int given_nthreads) : BinaryBank(given_filename,given_sizeElement,write) { nthreads = given_nthreads; //free(buffer); buffer =NULL; //cannot do that bufferT = (void **) malloc(sizeof(void*) * nthreads); for (int i= 0; i< nthreads; i++) { ((void ** )bufferT)[i]= (void *) malloc( WRITE_BUFFER); } cpt_buffer_tid = (int *)malloc(sizeof(int) * nthreads); memset (cpt_buffer_tid,0,sizeof(int) * nthreads); } void BinaryBankConcurrent::write_element_buffered( void *element, int tid) { write_buffered(element, sizeElement, tid); } void BinaryBankConcurrent::write_buffered( void *element, int size, int tid) { write_buffered( element, size, tid, true); } void BinaryBankConcurrent::write_buffered( void *element, int size, int tid, bool can_flush) { if(cpt_buffer_tid[tid]>= WRITE_BUFFER -100 && can_flush) { flush(tid); } char * buf_pt = ((char**) bufferT)[tid]; memcpy(buf_pt + cpt_buffer_tid[tid] , element, size); cpt_buffer_tid[tid]+=size; // cpt_buffer_tid[tid]++; } void BinaryBankConcurrent::flush(int tid) { flockfile(binary_read_file); if (!fwrite( ((void **)bufferT)[tid], 1, cpt_buffer_tid[tid], binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); funlockfile(binary_read_file); exit(1); } cpt_buffer_tid[tid]=0; funlockfile(binary_read_file); } //should be called by only one of the threads void BinaryBankConcurrent::close() { //flush buffer // if close Bank in read mode with data in the readbuffer, will result in error for(int ii=0; ii< nthreads; ii++) { if(cpt_buffer_tid[ii]) { if (!fwrite(((void **)bufferT)[ii], 1, cpt_buffer_tid[ii], binary_read_file)) // if (!fwrite(((void **)bufferT)[ii], sizeElement, cpt_buffer_tid[ii], binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } } cpt_buffer_tid[ii]=0; } fclose(binary_read_file); } void BinaryBank::write_element( void *element) { // flockfile(binary_read_file); // fprintf(stderr,"write elem %lli \n",*(int64_t *)element); if (!fwrite(element, sizeElement, 1, binary_read_file)) { // funlockfile(binary_read_file); printf("error: can't fwrite (disk full?)\n"); exit(1); } // funlockfile(binary_read_file); } void BinaryBank::write_element_buffered( void *element) { if(cpt_buffer==buffer_size_nelem) { if (!fwrite(buffer, sizeElement, buffer_size_nelem, binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } cpt_buffer=0; } //((kmer_type *)buffer)[cpt_buffer]= *((kmer_type *)element); memcpy((unsigned char *)buffer + (cpt_buffer * sizeElement), element, sizeElement); cpt_buffer++; } size_t BinaryBank::read_element( void *element) { return fread(element, sizeElement,1, binary_read_file); } size_t BinaryBank::read_element_buffered( void *element) { if(cpt_buffer==0) { cpt_buffer=fread(buffer, sizeElement,buffer_size_nelem, binary_read_file); if (cpt_buffer==0) return 0; cpt_init_buffer = cpt_buffer; } //memcpy(element, (unsigned char *)buffer + (cpt_buffer-1) * sizeElement, sizeElement);//ca les depile en sens inverse de la lecture memcpy(element, (unsigned char *)buffer + (cpt_init_buffer -1 - (cpt_buffer-1)) * sizeElement, sizeElement);//ca les depile dans le meme sens que la lecture cpt_buffer --; return cpt_buffer+1; // nb remaining before read } // used to read/write raw information to the binary file (e.g. kmer count) void BinaryBank::write( void *element, int size) { if (!fwrite(element, size, 1, binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } } size_t BinaryBank::read( void *element, int size) { return fread(element, size,1, binary_read_file); } void BinaryBank::rewind_all() { rewind(binary_read_file); } void BinaryBank::close() { //flush buffer // if close Bank in read mode with data in the readbuffer, will result in error if(cpt_buffer) { if (!fwrite(buffer, sizeElement, cpt_buffer, binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } } cpt_buffer=0; fclose(binary_read_file); } void BinaryBank::open(bool write) { binary_read_file = fopen(filename,write?"wb":"rb"); if( binary_read_file == NULL ) { char *buffer = (char*)malloc(BUFSIZ); strerror_r( errno, buffer, BUFSIZ ); // get string message from errno printf("error during fopen: %s write %i %s\n",buffer,write,filename); free(buffer); exit(1); } } off_t BinaryBank::nb_elements() { return fsize(filename)/sizeElement; } BinaryBank::~BinaryBank() { if(buffer!=NULL) { free (buffer); //buffer =NULL; } } BinaryBankConcurrent::~BinaryBankConcurrent() { for (int i= 0; i< nthreads; i++) { free(((void ** )bufferT)[i]); ((void ** )bufferT)[i]=NULL; } free(bufferT); } /////////////class BinaryReads a file containing reads BinaryReads::~BinaryReads() { free (buffer); buffer = NULL; } BinaryReads::BinaryReads(char *given_filename, bool write) { read_write_buffer_size = BINREADS_BUFFER; strcpy(filename,given_filename); open(write); buffer = (unsigned char *) malloc(read_write_buffer_size*sizeof(unsigned char)); cpt_buffer = 0; } void BinaryReads::rewind_all() { rewind(binary_read_file); } void BinaryReads::close() { unsigned int block_size =0; //flush buffer if(cpt_buffer) { //printf("close :write block %i \n",cpt_buffer); block_size = cpt_buffer; fwrite(&block_size, sizeof(unsigned int), 1, binary_read_file); // block header if (!fwrite(buffer, 1, cpt_buffer, binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } } cpt_buffer=0; fclose(binary_read_file); } void BinaryReads::open(bool write) { binary_read_file = fopen(filename,write?"wb":"rb"); if( binary_read_file == NULL ) { char *buffer = (char*)malloc(BUFSIZ); strerror_r( errno, buffer, BUFSIZ ); // get string message from errno printf("error during fopen: %s write %i %s\n",buffer,write,filename); free(buffer); exit(1); } } void BinaryReads::mark_newfile() { unsigned int block_size =0; //flush previous buffer if(cpt_buffer) { //printf("close :write block %i \n",cpt_buffer); block_size = cpt_buffer; fwrite(&block_size, sizeof(unsigned int), 1, binary_read_file); // block header if (!fwrite(buffer, 1, cpt_buffer, binary_read_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } } cpt_buffer=0; //then write empty block == mark of a new file block_size =0; fwrite(&block_size, sizeof(unsigned int), 1, binary_read_file); // block header with 0 } //format is // 32 bit integer = readlen, then seq in binary // then next read.. //32 bit len is overkill but simpler //also makes buffer then write block with header : size of block to read, with n reads .... will allow large fread when reading this file ... void BinaryReads::write_read(char * read, int readlen) { int tai = readlen; unsigned char rbin; char * pt = read; unsigned int block_size = 0; // printf("write read %i / %i readlen %i \n",cpt_buffer,read_write_buffer_size,readlen); //todo : also flush to disk sometimes (ie if very large buffer, to create smaller blocks..) if((cpt_buffer && cpt_buffer >= (read_write_buffer_size-readlen)) || cpt_buffer > 10000000 ) ////not enough space to store next read true space is 4 + readlen/4 + rem //flush buffer to disk { block_size = cpt_buffer; //printf("write block %i\n",block_size); if(block_size) fwrite(&block_size, sizeof(unsigned int), 1, binary_read_file); // block header if (!fwrite(buffer, 1, cpt_buffer, binary_read_file)) // write a block, it ends at end of a read { printf("error: can't fwrite (disk full?)\n"); exit(1); } cpt_buffer=0; } //check if still not enough space in empty buffer : can happen if large read, then enlarge buffer if(read_write_buffer_size < readlen) { read_write_buffer_size = 2*readlen; // too large but ok buffer = (unsigned char *) realloc(buffer,sizeof(unsigned char) * read_write_buffer_size); } memcpy(buffer+cpt_buffer,&readlen,sizeof(int)); cpt_buffer+= sizeof(int); //fwrite( (void *) &readlen, sizeof(int), 1, binary_read_file); for (tai=readlen; tai>=4 ; tai-=4) { rbin = code4NT(pt); // fwrite((void *) &rbin, 1,1,binary_read_file ); buffer[cpt_buffer]=rbin; cpt_buffer++; pt +=4; } //then remaining if(tai) { rbin = code_n_NT(pt,tai); // fwrite( (void *) &rbin,1,1,binary_read_file); buffer[cpt_buffer]=rbin; cpt_buffer++; } } void compute_kmer_table_from_one_seq(int readlen, char * seq, kmer_type * kmer_table ) //,char * pkmer_table //pour remplissage table loc { kmer_type graine = codeSeed(seq); kmer_type graine_revcomp = revcomp(graine); kmer_table[0] = min(graine,graine_revcomp); seq++; for (int i=1; i> 2) + ( ((kmer_type) comp_NT[NT2int(seq[sizeKmer-1])]) << (2*(sizeKmer-1)) ) ) & kmerMask ; kmer_table[i] = min(graine,graine_revcomp); seq++; } } ////kmers buffer KmersBuffer::KmersBuffer(BinaryReads *bfile, int pbuffer_size, int nseq_task ) { read_write_buffer_size = BINREADS_BUFFER; buffer = ( char *) malloc(read_write_buffer_size*sizeof( char)); cpt_buffer = 0; cpt_binSeq_read =0; binSeq_toread =0; max_read_length = KMERSBUFFER_MAX_READLEN; binfile = bfile; buffer_size = pbuffer_size; kmers_buffer = (kmer_type *) malloc(sizeof(kmer_type) * buffer_size); // binSeq = (char *) malloc(sizeof(char) * max_read_length); // no need to alloc ram for binse : will points to buffer binSeq_extended = (char *) malloc(sizeof(char) * max_read_length); blocksize_toread =0; nseq_step = nseq_task; binary_read_file = bfile->binary_read_file; } void KmersBuffer::reset_max_readlen(int read_length) { max_read_length = read_length; // binSeq = (char *) realloc(binSeq,sizeof(char) * max_read_length); binSeq_extended = (char *) realloc(binSeq_extended,sizeof(char) * max_read_length); } KmersBuffer::~KmersBuffer() { free (kmers_buffer); free(buffer); //free(binSeq); free(binSeq_extended); } //now returns number of kmers read int KmersBuffer::readkmers() { int llen; int * len = & llen ; unsigned int block_size =0; //////reading new block from disk if needed // cpt_buffer == blocksize_toread tells we finished reading previous buffer // (binSeq_toread <= cpt_binSeq_read) tells we finished reading the last sequence if(cpt_buffer == blocksize_toread && (binSeq_toread <= cpt_binSeq_read)) { flockfile(binary_read_file); if( ! fread(&block_size,sizeof(unsigned int),1, binary_read_file)) //read block header { funlockfile(binary_read_file); return -1; // no more blocks to read } // block_size = 0 is a marker to indicate new read file, when it happens return -2 if(block_size==0) { return -2 ; } /// if(block_size >= read_write_buffer_size) // block buffer need to be enlarged { read_write_buffer_size = 2*block_size; buffer = ( char *) realloc(buffer,sizeof( char) * read_write_buffer_size); } fread(buffer,sizeof( char),block_size, binary_read_file); // read a block of sequences into the buffer funlockfile(binary_read_file); cpt_buffer = 0; blocksize_toread = block_size; } /////////////////////// //now parse the whole block in ram int i,j; int nchar; unsigned char fournt; nkmers = 0; int nseq_lues = 0; //cpt_buffer : how much we have already read in the buffer //blocksize_toread : how much there is to read in the buffer while(cpt_buffer < blocksize_toread || ( binSeq_toread > cpt_binSeq_read)) //while work to do { if( binSeq_toread <= cpt_binSeq_read)// read new sequence if needed //we will put one sequence into binSeq_extended { memcpy(len,buffer+cpt_buffer,sizeof(int)); // the sequence length cpt_buffer += sizeof(int); nseq_lues ++; if( (*len) > max_read_length) reset_max_readlen((int)(1.2*(*len))); // resize memory for sequence if needed nchar = ((*len)+3)/4; // number of bytes used to encode the sequence in its binary format (4 nt per byte) binSeq = buffer + cpt_buffer; // point binseq to correct place //cpt_buffer == where we are now in the buffer cpt_buffer += nchar; // on disk data was encoded with 4 nucleotides per bytes, // here we expand one sequence to one nucl per byte into binSeq_extended //nucleotides are still encoded in [0-3] j=0; for(i=0; i> 2; binSeq_extended[j+2]=fournt & 3; fournt = fournt >> 2; binSeq_extended[j+1]=fournt & 3; fournt = fournt >> 2; binSeq_extended[j+0]=fournt & 3; j+=4; } binSeq_toread = *len-sizeKmer+1; // binSeq_toread tells how many kmers there are in this sequence cpt_binSeq_read = 0; // tells how many kmers we have currently parsed in this sequence } { // binSeq_extended = beginning of the sequence, // cpt_binSeq_read = how much we have already read in this sequence (when kmers_buffer is full, we can halt parsing kmers (see below) in the middle of a sequence, so this value is not necessarily 0) char *seq = binSeq_extended+cpt_binSeq_read; kmer_type graine; kmer_type graine_revcomp; if( binSeq_toread > cpt_binSeq_read) // there are still unread kmers in this sequence, here we read the first one, // we put it in graine / graine_revcomp and store it in the kmers_buffer { graine = codeSeed_bin(seq); graine_revcomp = revcomp(graine); if(nkmers>=buffer_size) { return nkmers; } kmers_buffer[nkmers] = min(graine,graine_revcomp); nkmers++; cpt_binSeq_read ++; seq++; } while( binSeq_toread > cpt_binSeq_read) //while there remains kmers to be read in this sequence { graine = (graine * 4 + (seq[sizeKmer-1])) & kmerMask ; //parse next nucleotide to construc the next kmer graine_revcomp = ((graine_revcomp >> 2) + ( ((kmer_type) comp_NT[(int)(seq[sizeKmer-1])]) << (2*(sizeKmer-1)) ) ) & kmerMask; kmers_buffer[nkmers] = min(graine,graine_revcomp); nkmers ++; cpt_binSeq_read ++; //we store the kmer in the kmers_buffer seq++; if(nkmers>=buffer_size) //the kmers_buffer is full, we stop { return nkmers; } } } } // we stop when we finished one block, or when kmers_buffer is full, // it can happen in the middle of a sequence : the next time we call readkmers we will have to continue // from where we stopped in this sequence (counter cpt_binSeq_read tells us that) //while buffer is non empty, we 'expand' a sequence into binSeq_extended //then we parse binSeq_extended to store kmers in the kmers_buffer return nkmers; } kissplice-2.4.0-p1/debruijn-v4/minia/Bank.h000644 000765 000024 00000011575 12676004527 020414 0ustar00ishistaff000000 000000 // // Bank.h // // Created by Guillaume Rizk on 28/11/11. // Modified by Rayan Chikhi on 16/2/13 // #ifndef Bank_h #define Bank_h #include #include "Kmer.h" #include // Added by Pierre Peterlongo on 02/08/2012. #define TAILLE_NOM 1024 #define MAX_NB_FILES 2000 #define BUFFER_SIZE 16384 // same as kseq.h #define WRITE_BUFFER 32768// 16384 //800000 #define KMERSBUFFER_MAX_READLEN 4096 // grows dynamically if needed #define BINREADS_BUFFER 100000 off_t fsize(const char *filename) ; // heavily inspired by kseq.h from Heng Li (https://github.com/attractivechaos/klib) typedef struct { gzFile stream; unsigned char *buffer; int buffer_start, buffer_end; bool eof; char last_char; char *fname; uint64_t estimated_filesize; } buffered_file_t; typedef struct { int length ,max; char *string; } variable_string_t; // supports opening multiple fasta/fastq files class Bank{ public: Bank(char *fname); Bank(char **fname, int nb_files_); void init(char **fname, int nb_files_); void close(); bool get_next_seq(char **nseq, int *len); bool get_next_seq_from_file(char **nseq, int *len, int file_id); bool get_next_seq_from_file(char **nseq, char **cheader, int *len, int *hlen, int file_id); bool get_next_seq(char **nseq, char **cheader, int *len, int *hlen); bool get_next_seq(char **nseq, char **cheader, int *len, int *hlen, int * id_file);//also return file id bool get_next_seq(char **nseq, int *len, int * id_file); //also return file id void open_stream(int i); // internal functions void close_stream(int i); void rewind_all(); variable_string_t *read, *dummy, *header; int nb_files; // total nb of files int index_file; // index of current file uint64_t filesizes; // estimate of total size for all files signed int buffered_gets(buffered_file_t *bf, variable_string_t *s, char *dret, bool append, bool allow_spaces); ~Bank(); uint64_t estimate_kmers_volume(int k); uint64_t estimate_nb_reads(); int estimate_max_readlen(); // functions that enable to read the same portion twice void save_position(); void load_position(); int restore_index_file; z_off_t restore_pos; buffered_file_t **buffered_file; }; class BinaryBank { protected: char filename[TAILLE_NOM]; FILE * binary_read_file; const int sizeElement; void * buffer; int cpt_buffer; int cpt_init_buffer; int buffer_size_nelem; public: BinaryBank(char *filename, int sizeElement, bool write); BinaryBank (); void write_element(void *element); size_t read_element(void *element); size_t read_element_buffered(void *element); void write( void *element, int size); void write_element_buffered(void *element); size_t read( void *element, int size); void rewind_all(); void close(); void open(bool write); off_t nb_elements(); ~BinaryBank(); }; class BinaryBankConcurrent : public BinaryBank { int * cpt_buffer_tid; // this counter is now in bytes int nthreads ; void * bufferT; public: BinaryBankConcurrent(char *given_filename, int given_sizeElement, bool write, int given_nthreads) ; void write_element_buffered(void *element, int tid); void write_buffered( void *element, int size, int tid); void write_buffered( void *element, int size, int tid, bool can_flush); void flush(int tid); void close(); ~BinaryBankConcurrent(); }; class BinaryReads { char filename[TAILLE_NOM]; // const int sizeElement; unsigned char * buffer; int cpt_buffer; unsigned int read_write_buffer_size; public: FILE * binary_read_file; BinaryReads(char *filename, bool write); // void write_element(void *element); //size_t read_element(void *element); void write_read(char * read, int readlen); void rewind_all(); void close(); void open(bool write); void mark_newfile(); ~BinaryReads(); }; class KmersBuffer { char * buffer; int cpt_buffer; int blocksize_toread; unsigned int read_write_buffer_size; int cpt_binSeq_read; int binSeq_toread; public: int max_read_length; BinaryReads * binfile; FILE * binary_read_file; int nkmers; //number of kmers in the buffer int nseq_step; int buffer_size; kmer_type * kmers_buffer; KmersBuffer(BinaryReads *bfile, int pbuffer_size, int nseq_task ); int readkmers(); char * binSeq;// [MAX_READ_LENGTH]; char * binSeq_extended;//[MAX_READ_LENGTH]; void reset_max_readlen(int read_length); ~KmersBuffer(); }; void compute_kmer_table_from_one_seq(int readlen, char * seq, kmer_type * kmer_table ) ; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Bloom.cpp000644 000765 000024 00000024677 12676004527 021153 0ustar00ishistaff000000 000000 // // Bloom.cpp // // Created by Guillaume Rizk on 9/02/12. // #include #include #include #include "Bloom.h" Bloom::Bloom() { //empty default constructor nb_elem = 0; blooma = NULL; } BloomCpt::BloomCpt() { //empty default constructor nb_elem = 0; blooma = NULL; } void Bloom::setSeed(uint64_t seed) { if(user_seed==0) { user_seed = seed; this->generate_hash_seed(); //regenerate the hash with the new seed } else{ fprintf(stderr,"Warning! you should not change the seed a second time!, resuming with previous seed %llu \n",(unsigned long long)user_seed); } } void Bloom::set_number_of_hash_func(int i) { if(i>NSEEDSBLOOM || i<1){ fprintf(stderr,"%i is not a valid value for number of hash funcs, should be in [1-%i], resuming wild old value %i\n",i,NSEEDSBLOOM,n_hash_func ); return; } n_hash_func = i; } void Bloom::generate_hash_seed() { unsigned int i; for ( i = 0; i < NSEEDSBLOOM; ++i) { seed_tab[i]= rbase[i]; } for ( i = 0; i < NSEEDSBLOOM; ++i) { seed_tab[i]= seed_tab[i] * seed_tab[(i+3) % NSEEDSBLOOM] + user_seed ; } } #ifdef _largeint inline uint64_t Bloom::hash_func(LargeInt elem, int num_hash) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits)] uint64_t result = 0, chunk, mask = ~0; LargeInt intermediate = elem; int i; for (i=0;i> 64; result ^= hash_func(chunk,num_hash); } return result; } #endif #ifdef _ttmath inline uint64_t Bloom::hash_func(ttmath::UInt elem, int num_hash) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits)] uint64_t result = 0, to_hash; ttmath::UInt intermediate = elem; uint32_t mask=~0, chunk; int i; for (i=0;i>= 32; (intermediate & mask).ToInt(chunk); to_hash |= ((uint64_t)chunk) << 32 ; intermediate >>= 32; result ^= hash_func(to_hash,num_hash); } return result; } #endif #ifdef _LP64 inline uint64_t Bloom::hash_func( __uint128_t elem, int num_hash) { // hash(uint128) = ( hash(upper 64 bits) xor hash(lower 64 bits)) return hash_func((uint64_t)(elem>>64),num_hash) ^ hash_func((uint64_t)(elem&((((__uint128_t)1)<<64)-1)),num_hash); } #endif inline uint64_t Bloom::hash_func( uint64_t key, int num_hash) { uint64_t hash = seed_tab[num_hash]; hash ^= (hash << 7) ^ key * (hash >> 3) ^ (~((hash << 11) + (key ^ (hash >> 5)))); hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1; hash = hash ^ (hash >> 24); hash = (hash + (hash << 3)) + (hash << 8); // hash * 265 hash = hash ^ (hash >> 14); hash = (hash + (hash << 2)) + (hash << 4); // hash * 21 hash = hash ^ (hash >> 28); hash = hash + (hash << 31); return hash; } //tai is 2^tai_bloom Bloom::Bloom(int tai_bloom) { n_hash_func = 4 ;//def user_seed =0; nb_elem = 0; tai = (1LL << tai_bloom); nchar = tai/8LL; blooma =(unsigned char *) malloc( nchar *sizeof(unsigned char)); // 1 bit per elem memset(blooma,0,nchar *sizeof(unsigned char)); //fprintf(stderr,"malloc Power-of-two bloom %lli MB nchar %llu %llu\n",(long long)((tai/8LL)/1024LL/1024LL),(unsigned long long)nchar,(unsigned long long)(tai/8)); this->generate_hash_seed(); } Bloom::Bloom(uint64_t tai_bloom) { //printf("custom construc \n"); n_hash_func = 4 ;//def user_seed =0; nb_elem = 0; tai = tai_bloom; nchar = (1+tai/8LL); blooma =(unsigned char *) malloc( nchar *sizeof(unsigned char)); // 1 bit per elem memset(blooma,0,nchar *sizeof(unsigned char)); //fprintf(stderr,"malloc bloom %lli MB \n",(tai/8LL)/1024LL/1024LL); this->generate_hash_seed(); } // //tai is 2^tai_bloom BloomCpt::BloomCpt(int tai_bloom) { n_hash_func = 2; user_seed = 0; nb_elem = 0; tai = (1LL << tai_bloom); blooma =(unsigned char *) malloc( (tai/2) *sizeof(unsigned char)); //4bits per elem memset(blooma,0,(tai/2) *sizeof(unsigned char)); fprintf(stderr,"malloc bloom cpt %lli MB \n",(tai/2LL)/1024LL/1024LL); this->generate_hash_seed(); } // //tai is 2^tai_bloom BloomCpt3::BloomCpt3(int tai_bloom) { n_hash_func = 2; user_seed = 0; nb_elem = 0; tai = (1LL << tai_bloom); //blooma =(unsigned char *) malloc( (tai/2) *sizeof(unsigned char)); //4bits per elem blooma3 = (uint64_t*) malloc( ((tai/21)+1) *sizeof(uint64_t)); //3bits per elem, 21 elem per uint64 memset(blooma3,0, ((tai/21)+1) *sizeof(uint64_t)); fprintf(stderr,"malloc bloom cpt64 3 bits %lli MB \n",8*(tai/21LL)/1024LL/1024LL); this->generate_hash_seed(); } // //tai is 2^tai_bloom BloomCpt2::BloomCpt2(int tai_bloom) { n_hash_func = 2; user_seed = 0; nb_elem = 0; tai = (1LL << tai_bloom); //blooma =(unsigned char *) malloc( (tai/2) *sizeof(unsigned char)); //4bits per elem blooma2 = (uint64_t*) malloc( (tai/32) *sizeof(uint64_t)); //2bits per elem, 32 elem per uint64 memset(blooma2,0, (tai/32) *sizeof(uint64_t)); fprintf(stderr,"malloc bloom cpt64 2 bits %lli MB \n",8*(tai/32LL)/1024LL/1024LL); this->generate_hash_seed(); } Bloom::~Bloom() { if(blooma!=NULL) free(blooma); } BloomCpt3::~BloomCpt3() { if(blooma3!=NULL) free(blooma3); } BloomCpt2::~BloomCpt2() { if(blooma2!=NULL) free(blooma2); } void Bloom::dump(char * filename) { FILE *file_data; file_data = fopen(filename,"wb"); fwrite(blooma, sizeof(unsigned char), nchar, file_data); //1+ printf("bloom dumped \n"); } void Bloom::load(char * filename) { FILE *file_data; file_data = fopen(filename,"rb"); printf("loading bloom filter from file, nelem %lli \n",nchar); fread(blooma, sizeof(unsigned char), nchar, file_data); printf("bloom loaded\n"); } long Bloom::weight() { // return the number of 1's in the Bloom, nibble by nibble const unsigned char oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; long weight = 0; for(uint64_t index = 0; index < nchar; index++) { unsigned char current_char = blooma[index]; weight += oneBits[current_char&0x0f]; weight += oneBits[current_char>>4]; } return weight; } void Bloom::add(bloom_elem elem) { uint64_t h1; int i; for(i=0; i> 3] |= bit_mask[h1 & 7]; } //nb_elem++; } int Bloom::contains(bloom_elem elem) { uint64_t h1; int i; for(i=0; i> 3 ] & bit_mask[h1 & 7]) != bit_mask[h1 & 7]) return 0; } return 1; } void BloomCpt::add(bloom_elem elem) { uint64_t h1; unsigned char val,cpt_per_key; int i; for(i=0; i> (4* (h1 & 1)) ; cpt_per_key++; if(cpt_per_key==16) cpt_per_key = 15; //satur at 15 val &= ~ cpt_mask[h1 & 1]; val |= cpt_per_key << (4* (h1 & 1)) ; blooma [h1 / cpt_per_char] = val; } } //9698232370296160 int BloomCpt::contains_n_occ(bloom_elem elem, int nks) { uint64_t h1; unsigned char cpt_per_key; int i; for(i=0; i> (4* (h1 & 1)); if(cpt_per_key> (3* (h1 %21)) ; cpt_per_key++; if(cpt_per_key==8) cpt_per_key = 7; //satur at 7 val &= ~ cpt_mask21[h1 % 21]; val |= cpt_per_key << (3* (h1 % 21)) ; blooma3 [h1 / 21] = val; // if(elem==9698232370296160) printf("--%016llX %i\n", val,cpt_per_key); } } int BloomCpt3::contains_n_occ(bloom_elem elem, int nks) { uint64_t h1; unsigned char cpt_per_key; int i; // printf("--contains-- \n"); // if(elem==9698232370296160) printf ("\nquery3 elem %lli \n",elem); for(i=0; i> (3* (h1 % 21)); //printf("%016llX\n", blooma3 [h1 / 21]); //printf("cpt %i \n", cpt_per_key); //if(elem==9698232370296160) printf("bloocpt3 %i \n", cpt_per_key); if(cpt_per_key> (2* (h1 & 31)) ; cpt_per_key++; if(cpt_per_key==4) cpt_per_key = 3; //satur at 3 val &= ~ cpt_mask32[h1 & 31]; val |= cpt_per_key << (2* (h1 & 31)) ; blooma2 [h1 / 32] = val; // if(elem==9698232370296160) printf("--%016llX %i\n", val,cpt_per_key); } } int BloomCpt2::contains_n_occ(bloom_elem elem, int nks) { uint64_t h1; unsigned char cpt_per_key; int i; for(i=0; i> (2* (h1 & 31)); if(cpt_per_key #include #include // not using kmer_type from Kmer.h because I don't want this class to depend on Kmer.h #ifdef _largeint #include "LargeInt.h" typedef LargeInt bloom_elem; #else #ifdef _ttmath #include "ttmath/ttmath.h" typedef ttmath::UInt bloom_elem; #else #if (! defined kmer_type) || (! defined _LP64) typedef uint64_t bloom_elem; #else typedef kmer_type bloom_elem; #endif #endif #endif #define NSEEDSBLOOM 10 #define CUSTOMSIZE 1 static const int bits_per_char = 0x08; // 8 bits in 1 char(unsigned) static const unsigned char bit_mask[bits_per_char] = { 0x01, //00000001 0x02, //00000010 0x04, //00000100 0x08, //00001000 0x10, //00010000 0x20, //00100000 0x40, //01000000 0x80 //10000000 }; static const int cpt_per_char = 2; static const unsigned char cpt_mask[cpt_per_char] = { 0x0F, //00001111 0xF0, //11110000 }; static const uint64_t cpt_mask21[21] = { 0x0000000000000007ULL,//00000....00000111 0x0000000000000038ULL, 0x00000000000001C0ULL, 0x0000000000000E00ULL, 0x0000000000007000ULL, 0x0000000000038000ULL, 0x00000000001C0000ULL, 0x0000000000E00000ULL, 0x0000000007000000ULL, 0x0000000038000000ULL, 0x00000001C0000000ULL, 0x0000000E00000000ULL, 0x0000007000000000ULL, 0x0000038000000000ULL, 0x00001C0000000000ULL, 0x0000E00000000000ULL, 0x0007000000000000ULL, 0x0038000000000000ULL, 0x01C0000000000000ULL, 0x0E00000000000000ULL, 0x7000000000000000ULL }; static const uint64_t cpt_mask32[32] = { 0x0000000000000003ULL,//00000....00000011 0x000000000000000CULL, 0x0000000000000030ULL,//00000....000110000 0x00000000000000C0ULL, 0x0000000000000300ULL, 0x0000000000000C00ULL, 0x0000000000003000ULL, 0x000000000000C000ULL, 0x0000000000030000ULL, 0x00000000000C0000ULL, 0x0000000000300000ULL, 0x0000000000C00000ULL, 0x0000000003000000ULL, 0x000000000C000000ULL, 0x0000000030000000ULL, 0x00000000C0000000ULL, 0x0000000300000000ULL, 0x0000000C00000000ULL, 0x0000003000000000ULL, 0x000000C000000000ULL, 0x0000030000000000ULL, 0x00000C0000000000ULL, 0x0000300000000000ULL, 0x0000C00000000000ULL, 0x0003000000000000ULL, 0x000C000000000000ULL, 0x0030000000000000ULL, 0x00C0000000000000ULL, 0x0300000000000000ULL, 0x0C00000000000000ULL, 0x3000000000000000ULL, 0xC000000000000000ULL }; /* static const unsigned char incr_cpt_table[2][255] = { {1, 2,3}, {3, 4,3}, }; */ static const uint64_t rbase[NSEEDSBLOOM] = { 0xAAAAAAAA55555555ULL, 0x33333333CCCCCCCCULL, 0x6666666699999999ULL, 0xB5B5B5B54B4B4B4BULL, 0xAA55AA5555335533ULL, 0x33CC33CCCC66CC66ULL, 0x6699669999B599B5ULL, 0xB54BB54B4BAA4BAAULL, 0xAA33AA3355CC55CCULL, 0x33663366CC99CC99ULL }; /* 0x2E7E5A8996F99AA5, 0x74B2E1FB222EFD24, 0x8BBE030F6704DC29, 0x6D8FD7E91C11A014, 0xFC77642FF9C4CE8C, 0x318FA6E7C040D23D, 0xF874B1720CF914D5, 0xC569F575CDB2A091, */ //static uint64_t pri1=0x5AF3107A401FULL; //static uint64_t pri2 =0x78C27CE77ULL; class Bloom{ protected: #ifdef _largeint inline uint64_t hash_func(LargeInt elem, int num_hash); #endif #ifdef _ttmath inline uint64_t hash_func(ttmath::UInt elem, int num_hash); #endif #ifdef _LP64 inline uint64_t hash_func(__uint128_t key, int num_hash); #endif inline uint64_t hash_func(uint64_t key, int num_hash); inline void generate_hash_seed(); uint64_t user_seed; uint64_t seed_tab[NSEEDSBLOOM]; int n_hash_func; uint64_t nchar; public: unsigned char * blooma; void setSeed(uint64_t seed) ; void set_number_of_hash_func(int i) ; void add(bloom_elem elem); int contains(bloom_elem elem); uint64_t tai; uint64_t nb_elem; void dump(char * filename); void load(char * filename); long weight(); Bloom(int tai_bloom); Bloom(uint64_t tai_bloom); Bloom(); ~Bloom(); }; class BloomCpt: public Bloom { public : BloomCpt(int tai_bloom); BloomCpt(); void add(bloom_elem elem); int contains_n_occ(bloom_elem elem, int nks); }; class BloomCpt3: public BloomCpt { public : BloomCpt3(int tai_bloom); ~BloomCpt3(); uint64_t * blooma3; void add(bloom_elem elem); int contains_n_occ(bloom_elem elem, int nks); }; class BloomCpt2: public BloomCpt { public : BloomCpt2(int tai_bloom); ~BloomCpt2(); uint64_t * blooma2; void add(bloom_elem elem); int contains_n_occ(bloom_elem elem, int nks); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Debloom.cpp000644 000765 000024 00000036275 12676004527 021461 0ustar00ishistaff000000 000000 #include "Debloom.h" // GUS: the false positive set can be either FPSet or FPSetCascading4, // both inheriting from Set. The choice is made by the function called // to load the false positives: load_false_positives() or // load_false_positives_cascading4(). Set *false_positives; FILE * F_debloom_read; FILE * F_debloom_write; uint64_t n_false_positives=0; Hash16 * hasht1; void end_debloom_partition(bool last_partition) { int value; char false_positive_kmer_char[sizeKmer+1]; FILE *file_false_positive_kmers =NULL; kmer_type graine; /////////////////////////begin write files rewind (F_debloom_read); rewind (F_debloom_write); #ifndef MINGW ftruncate(fileno(F_debloom_write), 0); //erase previous file #else // tempfix? fileno is not accepted by mingw fclose(F_debloom_write); F_debloom_write = fopen(return_file_name("debloom2"),"wb+"); #endif if (last_partition) { // write false positive kmers to fasta file file_false_positive_kmers = fopen(return_file_name(false_positive_kmers_file),"wb"); } n_false_positives = 0; while(fread(&graine, sizeof(graine),1, F_debloom_read)){ if(hasht1->get(graine,&value)==0) //kmer not present == kmer not solid { n_false_positives ++; if (!fwrite(&graine, sizeof(graine), 1, F_debloom_write)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } if (last_partition) { code2seq(graine,false_positive_kmer_char); fprintf(file_false_positive_kmers,">fp\n"); fputs(false_positive_kmer_char,file_false_positive_kmers); fprintf(file_false_positive_kmers,"\n"); } } //else kmer is a true positive, do nothing } if (last_partition) fclose(file_false_positive_kmers); } int debloom(int order, int max_memory) { // read bloo1 from disk dump Bloom *bloo1 = bloom_create_bloo1((BloomCpt *)NULL); STARTWALL(pos); FILE * debloom_file = fopen(return_file_name("debloom"),"wb+"); FILE * debloom_file_2 = fopen(return_file_name("debloom2"),"wb+"); FILE * F_tmp; F_debloom_read = debloom_file; F_debloom_write = debloom_file_2; BinaryBank *SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer_type),0); uint64_t cc=0; kmer_type new_graine, kmer; int nt; uint64_t NbSolidKmer =0; // write all positive extensions in disk file while (SolidKmers->read_element(&kmer)) { //8 right extensions (4F and 4R); left extensions are redundant by revcomplementation for(nt=0; nt<4; nt++) { int strand; for (strand = 0; strand < 2 ; strand++) { int current_strand = strand; new_graine = next_kmer(kmer,nt, ¤t_strand); if(bloo1->contains(new_graine)){ // extension is positive // maybe do more lax deblooming; if it's a dead-end, it's no big deal, don't pass it to the false positive test // what would have been needed if i decided to enable order>0 (but actually this won't happen): // - better estimate of structure size in the presence of order>0 deblooming if (order == 1) // this case just detects tips { bool is_linked = false; for(int tip_nt=0; tip_nt<4; tip_nt++) { int new_strand = current_strand; kmer_type kmer_after_possible_tip = next_kmer(new_graine,tip_nt, &new_strand); if(bloo1->contains(kmer_after_possible_tip)) { is_linked = true; break; } } if (!is_linked) continue; // it's a tip, because it's linked to nothing } if (order > 1) // general case. should work for order = 1, but i coded an optimized version above { Frontline frontline( new_graine, current_strand, bloo1, NULL, NULL, NULL); while (frontline.depth < order) { frontline.go_next_depth(); if (frontline.size() == 0) break; // don't allow a breadth too large anywqy if (frontline.size()> 10) break; } if (frontline.size() == 0) continue; // it's a deadend } if (!fwrite(&new_graine, sizeof(new_graine), 1, debloom_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } cc++; } } } NbSolidKmer++; if ((NbSolidKmer%10000)==0) fprintf (stderr,"%c Writing positive Bloom Kmers %lld",13,NbSolidKmer); } nbkmers_solid = NbSolidKmer; // GUS: it's global now fprintf(stderr,"\n%lli kmers written\n",cc); STOPWALL(pos,"Write all positive kmers"); STARTWALL(deb); double bl1tai = (double)bloo1->tai ; delete bloo1; // now that bloo1 is deleted, initialize hasht1 int NBITS_HT = max( (int)ceilf(log2f((0.1*max_memory*1024L*1024L)/sizeof(cell_ptr_t))), 1); // set hasht1 cells to occupy 0.1 * [as much mem as poss] hasht1 =new Hash16(NBITS_HT); //////////////////////////////////////////////////////////////// --find false positive, with hash table partitioning uint64_t max_kmer_per_part = (uint64_t) (0.8*max_memory*1024LL*1024LL /sizeof(cell)); //adapter taille ht en fonction printf("%d partitions will be needed\n",(int)(nbkmers_solid/max_kmer_per_part)); NbSolidKmer =0; int numpart = 0; SolidKmers->rewind_all(); // deblooming: // read the list of (non-redundant) solid kmers and load it, in chunks, into a hash table // at each pass, check all the positive extensions and keep those which are not indicated, by the current chunk, as solid kmers // at the end, only the positive extensions which are not solid are kept while (SolidKmers->read_element(&kmer)) { hasht1->add(kmer); NbSolidKmer++; if ((NbSolidKmer%10000)==0) fprintf (stderr,"%cBuild Hash table %lld",13,NbSolidKmer); if(hasht1->nb_elem >max_kmer_per_part) //end partition, find false positives { fprintf(stderr,"End of debloom partition %lli / %lld \n",hasht1->nb_elem,max_kmer_per_part); end_debloom_partition(false); //swap file pointers F_tmp = F_debloom_read; F_debloom_read = F_debloom_write; F_debloom_write = F_tmp; /////////end write files //reset hash table hasht1->empty_all(); fprintf(stderr,"\n%lli false positives written , partition %i \n",n_false_positives,numpart); numpart++; } ///end partition } //fprintf(stderr,"Nb kmers stored in the bloom table %lld\n",nbkmers_solid); ///////////////////////// last partition, will write all the FP's to the good file end_debloom_partition(true); /////////end write files fprintf(stderr,"Total nb false positives stored in the Debloom hashtable %lli \n",n_false_positives); delete hasht1; STOPWALL(deb,"Debloom"); // GUS: will use to output summary later b1_size = (uint64_t) bl1tai; fclose(debloom_file); fclose(debloom_file_2); SolidKmers->close(); return 1; } uint64_t countFP(Bank *FalsePositives) { char * rseq; int readlen; uint64_t nbFP = 0; while (FalsePositives->get_next_seq(&rseq,&readlen)) nbFP++; FalsePositives->rewind_all(); return nbFP; } Set *load_false_positives() { int64_t NbInsertedKmers = 0; char * rseq; int readlen; kmer_type kmer, graine, graine_revcomp; Bank *FalsePositives = new Bank(return_file_name(false_positive_kmers_file)); // alloc false positives with the just the right estimated size uint64_t nbFP = countFP(FalsePositives); FPSet *fp = new FPSet(nbFP); while (FalsePositives->get_next_seq(&rseq,&readlen)) { kmer = extractKmerFromRead(rseq,0,&graine,&graine_revcomp); fp->insert(kmer); NbInsertedKmers++; if ((NbInsertedKmers%10000)==0) fprintf (stderr,(char*)"%cInsert false positive Kmers in hash table %lld",13,NbInsertedKmers); } fp->finalize(); // always call this when finishing to create a FPSet fprintf (stderr,"\nInserted %lld false positive kmers in the hash structure.\n\n",NbInsertedKmers); // print_size_summary(fp); return fp; } Set *dummy_false_positives() { FPSet *fp = new FPSet((uint64_t)1); return fp; } Set *load_false_positives_cascading4() { int64_t NbInsertedKmers; char * rseq; int readlen; kmer_type kmer, graine, graine_revcomp; // **** Initialize B2, B3, B4 and T4 **** Bank *FalsePositives = new Bank(return_file_name(false_positive_kmers_file)); uint64_t nbFP = countFP(FalsePositives); FPSetCascading4 *fp = new FPSetCascading4; fp->bloom2 = new Bloom((uint64_t)(nbFP * NBITS_PER_KMER)); fp->bloom2->set_number_of_hash_func((int)floorf(0.7*NBITS_PER_KMER)); uint64_t estimated_T2_size = max((int)ceilf(nbkmers_solid * (double)powf((double)0.62, (double)NBITS_PER_KMER)), 1); uint64_t estimated_T3_size = max((int)ceilf(nbFP * (double)powf((double)0.62, (double)NBITS_PER_KMER)) ,1); fp->bloom3 = new Bloom((uint64_t)(estimated_T2_size * NBITS_PER_KMER)); fp->bloom3->set_number_of_hash_func((int)floorf(0.7*NBITS_PER_KMER)); fp->bloom4 = new Bloom((uint64_t)(estimated_T3_size * NBITS_PER_KMER)); fp->bloom4->set_number_of_hash_func((int)floorf(0.7*NBITS_PER_KMER)); // **** Insert the false positives in B2 **** NbInsertedKmers = 0; while (FalsePositives->get_next_seq(&rseq,&readlen)) { kmer = extractKmerFromRead(rseq,0,&graine,&graine_revcomp); fp->bloom2->add(kmer); NbInsertedKmers++; // if ((NbInsertedKmers%10000)==0) // fprintf (stderr,"%cInsert false positive B2 %lld",13,NbInsertedKmers); } //fprintf (stderr,"%cInsert false positive B2 %lld", 13,NbInsertedKmers); FalsePositives->close(); DEBUGE(("\nInserted %lld (estimated, %lld) kmers in B2.\n", NbInsertedKmers, nbFP)); // **** Insert false positives in B3 and write T2 int addKmers = 0; NbInsertedKmers = 0; FILE *T2_file = fopen(return_file_name("t2_kmers"), "w+"); // We will read this file later, when filling T4 BinaryBank *SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer),0); while(SolidKmers->read_element(&kmer)) { if (fp->bloom2->contains(kmer)) { if (!fwrite(&kmer, sizeof(kmer), 1, T2_file)) { printf("error: can't fwrite (disk full?)\n"); exit(1); } fp->bloom3->add(kmer); addKmers++; } NbInsertedKmers++; //if ((NbInsertedKmers%10000)==0) //fprintf (stderr,(char*)"%cInsert false positive B3 %lld",13,NbInsertedKmers); } // fprintf (stderr,(char*)"%cInsert false positive B3 %lld",13,NbInsertedKmers); SolidKmers->close(); DEBUGE(("\nInserted %d (estimated, %llu) kmers in B3.\n", addKmers, estimated_T2_size)); // **** Insert false positives in B4 (we could write T3, but it's not necessary) FalsePositives = new Bank(return_file_name(false_positive_kmers_file)); NbInsertedKmers = 0; addKmers = 0; while (FalsePositives->get_next_seq(&rseq,&readlen)) { kmer = extractKmerFromRead(rseq,0,&graine,&graine_revcomp); if (fp->bloom3->contains(kmer)) { fp->bloom4->add(kmer); addKmers++; } NbInsertedKmers++; //if ((NbInsertedKmers%10000)==0) //fprintf (stderr,"%cInsert false positive B4 %lld",13,NbInsertedKmers); } //fprintf (stderr,"%cInsert false positive B4 %lld", 13,NbInsertedKmers); FalsePositives->close(); DEBUGE(("\nInserted %d (estimated, %lld) kmers in B4.\n", addKmers, estimated_T3_size)); // **** Count and insert false positives in T4 rewind(T2_file); addKmers = 0; while (fread(&kmer, sizeof(kmer), 1, T2_file)) if (fp->bloom4->contains(kmer)) addKmers++; fp->false_positives = new FPSet(addKmers); rewind(T2_file); addKmers = 0; NbInsertedKmers = 0; while (fread(&kmer, sizeof(kmer), 1, T2_file)) { if (fp->bloom4->contains(kmer)) { fp->false_positives->insert(kmer); addKmers++; } NbInsertedKmers++; // if ((NbInsertedKmers%10000)==0) // fprintf (stderr,"%cInsert false positive T4 %lld",13,NbInsertedKmers); } fp->false_positives->finalize(); // fprintf (stderr,"%cInsert false positive T4 %lld", 13,NbInsertedKmers); fclose(T2_file); DEBUGE(("\nInserted %d (estimated, %lld) kmers in T4.\n\n", addKmers, (uint64_t)fp->false_positives->capacity())); // print_size_summary(fp); return fp; } double toMB(double value) { return value / 8LL/1024LL/1024LL; } void print_size_summary(FPSet *fp) { int bits_per_FP_element = FPSet::bits_per_element; uint64_t size_B1 = b1_size, size_T1 = fp->capacity() * FPSet::bits_per_element; double total_size = (double)(size_B1 + size_T1); fprintf(stderr,"Size of the Bloom table : %.2lf MB\n", toMB(size_B1) ); fprintf(stderr," %.2lf bits / solid kmer\n", b1_size/(double)(nbkmers_solid) ); fprintf(stderr, "Size of the FP table : %lli FP x %d bits = %.2lf MB \n", fp->capacity(), bits_per_FP_element, toMB((double)(size_T1)) ); fprintf(stderr," actual implementation : %.2lf bits / solid kmer\n", size_T1/(double)nbkmers_solid); fprintf(stderr," assuming list of kmers, i.e. sizeof(kmer_type) bits / FP : %.2lf bits / solid kmer \n\n",(fp->capacity()*sizeof(kmer_type)*8LL)/(double)(nbkmers_solid)); fprintf(stderr," Total %.2lf MB for %lld solid kmers ==> %.2lf bits / solid kmer\n\n", toMB(total_size), nbkmers_solid, total_size / nbkmers_solid); } void print_size_summary(FPSetCascading4 *fp) { uint64_t size_B1 = b1_size, size_B2 = fp->bloom2->tai, size_B3 = fp->bloom3->tai, size_B4 = fp->bloom4->tai, size_T4 = fp->false_positives->capacity() * FPSet::bits_per_element; double total_size = (double)(size_B1 + size_B2 + size_B3 + size_B4 + size_T4); DEBUGE((stderr,"Size of the Bloom table (B1) : %.2lf MB\n", toMB((double)size_B1))); DEBUGE((stderr,"Size of the Bloom table (B2) : %.2lf MB\n", toMB((double)size_B2))); DEBUGE((stderr,"Size of the Bloom table (B3) : %.2lf MB\n", toMB((double)size_B3))); DEBUGE((stderr,"Size of the Bloom table (B4) : %.2lf MB\n", toMB((double)size_B4))); DEBUGE((stderr,"Size of the FP table (T4) : %.2lf MB\n", toMB((double)size_T4))); fprintf(stderr," Total %.2lf MB for %lld solid kmers ==> %.2lf bits / solid kmer\n\n", toMB(total_size), nbkmers_solid, total_size / nbkmers_solid); } kissplice-2.4.0-p1/debruijn-v4/minia/Debloom.h000644 000765 000024 00000001671 12676004527 021116 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include // for log2f #include // for max #include // for truncate #ifndef DEBLOOM_H #define DEBLOOM_H #include "Bank.h" #include "Bloom.h" #include "Kmer.h" #include "Hash16.h" #include "Utils.h" #include "Traversal.h" #define DEBUGE(a) //printf a using namespace std; extern uint64_t b1_size ; extern uint64_t nbkmers_solid ; typedef ListSet FPSet; // list-based //typedef HashSet FPSet; // hash-based // GUS: see comment in Debloom.cpp, where false_positive is been declared extern Set *false_positives; int debloom(int order, int max_memory); void end_debloom_partition(bool last_partition); Set *dummy_false_positives(); Set *load_false_positives(); Set *load_false_positives_cascading4(); void print_size_summary(FPSet *fp); void print_size_summary(FPSetCascading4 *fp); #endif kissplice-2.4.0-p1/debruijn-v4/minia/GraphOutput.cpp000644 000765 000024 00000035275 12676004527 022361 0ustar00ishistaff000000 000000 #include "GraphOutput.h" const string GraphOutput::graph_file_suffix = ".graph"; const string GraphOutput::nodes_file_suffix = ".nodes"; const string GraphOutput::edges_file_suffix = ".edges"; const string GraphOutput::xml_file_suffix = ".xgmml"; const string GraphOutput::json_nodes_file_suffix = ".json_nodes"; const string GraphOutput::json_edges_file_suffix = ".json_edges"; const string GraphOutput::json_file_suffix = ".json"; /************************************************************************************************************************/ /* init function initialize the files need to construct graph file or sequences file */ /* */ /************************************************************************************************************************/ void GraphOutput::init(bool erase){ //printf("create a graph erase=%s graph_format=%d first_id_nodes=%d first_id_edges=%d\n"", erase?"true":"false", graph_format, first_id_els.node, first_id_els.edge); graph_file_name=(prefix+graph_file_suffix); nodes_file_name=(prefix+nodes_file_suffix); edges_file_name=(prefix+edges_file_suffix); xml_file_name=(prefix+xml_file_suffix); json_nodes_file_name=(prefix+json_nodes_file_suffix); json_edges_file_name=(prefix+json_edges_file_suffix); json_file_name=(prefix+json_file_suffix); switch (graph_format){ case 0:// FORMAT .GRAPH graph_file = fopen(graph_file_name.c_str(),erase?"w":"a"); fprintf(graph_file,"digraph dedebruijn {\n"); break; case 1: // FORMAT .NODES AND .EDGES nodes_file = fopen(nodes_file_name.c_str(),erase?"w":"a"); edges_file = fopen(edges_file_name.c_str(),erase?"w":"a"); break; case 2 :// FORMAT .XGMML graph_file = fopen(xml_file_name.c_str(),erase?"w":"a"); //fprintf(graph_file,"\n\n\n"); fprintf(graph_file,"\n"); break; case 3: // FORMAT .json nodes_file = fopen(json_nodes_file_name.c_str(),erase?"w":"a"); edges_file = fopen(json_edges_file_name.c_str(),erase?"w":"a"); graph_file = fopen(json_file_name.c_str(),erase?"w":"a"); break; } } /************************************************************************************************************************/ /* printf GraphOutput and initialize files (files are not erasing) */ /* */ /************************************************************************************************************************/ GraphOutput::GraphOutput(string prefix, int graph_format, id_els first_id_els) : prefix(prefix), graph_format(graph_format), first_id_els(first_id_els) { // PIERRE: need something different than 0 for the first node printf("graph_format=%d first_id_nodes=%d first_id_edges=%d\n", graph_format, first_id_els.node, first_id_els.edge); init(true); } /************************************************************************************************************************/ /* Initialize first elements and files (files are erasing) */ /* */ /************************************************************************************************************************/ GraphOutput::GraphOutput(string prefix, int graph_format) : prefix(prefix), graph_format(graph_format) { first_id_els.node=0; first_id_els.edge=0; printf("graph_format=%d first_id_nodes=%d first_id_edges=%d\n", graph_format, first_id_els.node, first_id_els.edge); init(true); } /************************************************************************************************************************/ /* write graph file or sequence file */ /* */ /************************************************************************************************************************/ void GraphOutput::close() { switch (graph_format){ case 0: fprintf(graph_file,"}\n"); fclose(graph_file); break; case 1: fclose(nodes_file); fclose(edges_file); break; case 2: fprintf(graph_file,"\n"); fclose(graph_file); break; case 3: // We need to store all nodes and then all edges in the final .json file fclose(nodes_file); fclose(edges_file); ifstream nodes(json_nodes_file_name.c_str(), ios::in); ifstream edges(json_edges_file_name.c_str(), ios::in); if(!edges || !nodes){fprintf(stderr,"Cannot open file %s, %s or %s, exit\n", json_edges_file_suffix.c_str(), json_nodes_file_suffix.c_str()); exit(1);} string line; fprintf(graph_file,"{\n \"Starter\":[\n{"); fprintf(graph_file,"\n \"nodes\": [\n"); getline(nodes,line); fprintf(graph_file,"%s",line.c_str()); // prints the first node without comma before //for each node while(getline(nodes,line)){ fprintf(graph_file,",\n%s",line.c_str()); // prints the other nodes }; fprintf(graph_file,"\n],\n"); fprintf(graph_file,"\"edges\": [\n"); getline(edges,line); fprintf(graph_file,"%s",line.c_str()); // prints the first edge without comma before //for each edge while(getline(edges,line)) { fprintf(graph_file,",\n%s",line.c_str()); // prints the others edges }; //end of graph file en close file fprintf(graph_file,"\n]\n}\n"); nodes.close(); remove(json_nodes_file_name.c_str()); edges.close(); remove(json_edges_file_name.c_str()); fclose(graph_file); } } /************************************************************************************************************************/ /* recalculate length for a node (more efficient than capture length in string and convert the in integer) */ /* */ /************************************************************************************************************************/ long GraphOutput::sequence_length(string line) { string seq_char; int err,match,start, end; regex_t preg; long seq_len=0; size_t nmatch, size; const char *str_regex ="([A-Z]+)"; //regex capture sequences characters const char *line_c =NULL; line_c = line.c_str(); err = regcomp (&preg, str_regex, REG_EXTENDED); if (err == 0)//security for error string snapshot and if regex match { nmatch = 0; nmatch = preg.re_nsub; regmatch_t *pmatch=NULL; pmatch = (regmatch_t*) malloc (sizeof (*pmatch) * nmatch); if (pmatch) { match = regexec (&preg, line_c, nmatch, pmatch, 0); regfree (&preg); if (match == 0) { char *seq_char =NULL; start = pmatch[0].rm_so; end = pmatch[0].rm_eo; size = end - start; seq_len = sizeof(line_c[start])*(size); } } } else { fprintf (stderr, "LOW MEMORY !\n"); exit (EXIT_FAILURE); } return seq_len; } /************************************************************************************************************************/ /* output a single node to a file */ /* */ /************************************************************************************************************************/ void GraphOutput::print_node(long index, char *ascii_node) // output a single node to a file { int len; switch (graph_format){ case 0: // DOT format fprintf(graph_file,"%ld [label=\"%s\"];\n",index,ascii_node); break; case 1: // kissplice format fprintf(nodes_file,"%ld\t%s\n",index,ascii_node); break; case 2: // XGMML format fprintf(graph_file,"\n\n",index,ascii_node); break; case 3: // json format string seq = ascii_node; len = seq.size(); fprintf(nodes_file," { \"data\": { \"id\":\"%ld\", \"length\":%d, \"sequence\":\"%s\"}}\n",index,len,ascii_node); break; } } /************************************************************************************************************************/ /* output a single edges to a file */ /* */ /************************************************************************************************************************/ void GraphOutput::print_edge(long index, long id, long id2, string label) { switch (graph_format){ case 0: // DOT format fprintf(graph_file,"%ld -> %ld [label=\"%s\"];\n",id,id2,label.c_str()); break; case 1: // kissplice format fprintf(edges_file,"%ld\t%ld\t%s\n",id,id2,label.c_str()); break; case 2: // XGMML format fprintf(graph_file,"\n\n",id,id2,label.c_str()); break; case 3: // json format fprintf(edges_file,"{ \"data\":{ \"id\": \"%ld\", \"source\": \"%ld\",\"target\": \"%ld\",\"direction\": \"%s\"}}\n",index, id,id2,label.c_str()); //fprintf(edges_file,"{ \"data\":{ \"source\": %ld,\"target\": %ld,\"direction\": \"%s\"}}\n",id,id2,label.c_str()); break; } } /************************************************************************************************************************/ /* load nodes extremities */ /* */ /************************************************************************************************************************/ void GraphOutput::load_nodes_extremities(string linear_seqs_name) { kmer_links.clear(); // PIERRE: reset previous stored kmer links Bank *Nodes = new Bank((char *)linear_seqs_name.c_str()); long nb_nodes = first_id_els.node; //PIERRE; char * rseq; int readlen; sizeKmer--; // nodes extremities overlap by (k-1)-mers, so let's extract (k-1)-mers while (Nodes->get_next_seq(&rseq,&readlen)) { kmer_type left_kmer, right_kmer, left_kmer_fw, left_kmer_rc, right_kmer_fw, right_kmer_rc; left_kmer = extractKmerFromRead(rseq,0,&left_kmer_fw,&left_kmer_rc, false); right_kmer = extractKmerFromRead(rseq,readlen-sizeKmer,&right_kmer_fw,&right_kmer_rc, false); Strand left_strand = (left_kmer == left_kmer_fw)?FW:RC; Strand right_strand = (right_kmer == right_kmer_fw)?FW:RC; kmer_links[left_kmer].insert(node_strand(nb_nodes, left_strand, LEFT)); kmer_links[right_kmer].insert(node_strand(nb_nodes, right_strand, RIGHT)); nb_nodes++; } Nodes->close(); delete Nodes; sizeKmer++; // make sure to restore k } /************************************************************************************************************************/ /* construct node file and edge file for graph file */ /* */ /************************************************************************************************************************/ id_els GraphOutput::construct_graph(string linear_seqs_name) // PIERRE: i need to know the last nb_nodes { Bank *Nodes = new Bank((char *)linear_seqs_name.c_str()); id_els nb_els = first_id_els; //Alexan: stucture for print id elements in graph output char * rseq; int readlen; Nodes->rewind_all(); sizeKmer--; // nodes extremities overlap by (k-1)-mers, so let's extract (k-1)-mers // for each node, output all the out-edges (in-edges will correspond to out-edges of neighbors) while (Nodes->get_next_seq(&rseq,&readlen)) { kmer_type left_kmer, right_kmer, left_kmer_fw, left_kmer_rc, right_kmer_fw, right_kmer_rc; set::iterator it; left_kmer = extractKmerFromRead(rseq,0,&left_kmer_fw,&left_kmer_rc, false); right_kmer = extractKmerFromRead(rseq,readlen-sizeKmer,&right_kmer_fw,&right_kmer_rc, false); Strand left_strand = (left_kmer == left_kmer_fw)?FW:RC; Strand right_strand = (right_kmer == right_kmer_fw)?FW:RC; // left edges (are revcomp extensions) for (it = kmer_links[left_kmer].begin(); it != kmer_links[left_kmer].end(); it++) { long cur_node = it->node; Strand cur_strand = it->strand; LeftOrRight cur_left_or_right = it->left_or_right; if (cur_node ==nb_els.node) // prevent self loops on same kmer if (readlen == sizeKmer) continue; string label = "R"; if (cur_left_or_right == LEFT) { if (cur_strand != left_strand) label+=(string)"F"; else continue; } else { if (cur_strand == left_strand) label+=(string)"R"; else continue; } print_edge(nb_els.edge, nb_els.node,cur_node,label); nb_els.edge++; } // right edges for (it = kmer_links[right_kmer].begin(); it != kmer_links[right_kmer].end(); it++) { long cur_node = it->node; Strand cur_strand = it->strand; LeftOrRight cur_left_or_right = it->left_or_right; if (cur_node == nb_els.node) // prevent self loops on same kmer if (readlen == sizeKmer) continue; string label = "F"; if (cur_left_or_right == LEFT) { if (cur_strand == right_strand) label+=(string)"F"; else continue; } else { if (cur_strand != right_strand) label+=(string)"R"; else continue; } print_edge(nb_els.edge, nb_els.node,cur_node,label); nb_els.edge++; } //nodes print_node(nb_els.node, rseq); nb_els.node++; } sizeKmer++; // make sure to restore k Nodes->close(); delete Nodes; return nb_els; } kissplice-2.4.0-p1/debruijn-v4/minia/GraphOutput.h000644 000765 000024 00000012724 12676004527 022020 0ustar00ishistaff000000 000000 #include #include #include #include // for exit() #include #include #include #include #include #include #include #include "../minia/Kmer.h" #include "../minia/Bank.h" #ifndef _GRAPHOUTPUT_H #define _GRAPHOUTPUT_H using namespace std; // hash functions for unordered_map with various kmer_type's namespace std { //structure for print id nodes and edges in graph output struct id_els{ long node; long edge; }; #ifdef _LP64 template <> struct hash<__uint128_t> : public unary_function<__uint128_t, size_t> { size_t operator()(const __uint128_t& elem) const { hash hash_func; return hash_func((uint64_t)(elem>>64)) ^ hash_func((uint64_t)(elem&((((__uint128_t)1)<<64)-1))); } }; #endif #ifdef _ttmath template <> struct hash > : public unary_function, size_t> { size_t operator()(const ttmath::UInt& elem) const { hash hash_func; // hash = XOR_of_series[hash(i-th chunk iof 64 bits) uint64_t result = 0, to_hash; ttmath::UInt intermediate = elem; uint32_t mask=~0, chunk; int i; for (i=0;i>= 32; (intermediate & mask).ToInt(chunk); to_hash |= ((uint64_t)chunk) << 32 ; intermediate >>= 32; result ^= hash_func(to_hash); } return result; } }; #endif #ifdef _largeint template <> struct hash > : public unary_function, size_t> { size_t operator()(const LargeInt& elem) const { hash hash_func; // hash = XOR_of_series[hash(i-th chunk iof 64 bits) uint64_t result = 0, to_hash; LargeInt intermediate = elem; uint32_t mask=~0, chunk; int i; for (i=0;i> 32; chunk = (intermediate & mask).toInt(); to_hash |= ((uint64_t)chunk) << 32 ; intermediate = intermediate >> 32; result ^= hash_func(to_hash,num_hash); } return result; } }; #endif } class GraphOutput { public: string prefix; string graph_file_name; string nodes_file_name; string edges_file_name; string json_starters_file_name; string xml_file_name; string json_nodes_file_name; string json_edges_file_name; string json_file_name; int graph_format; id_els first_id_els; long edge_id; // the json format needs an id on the nodes. static const string graph_file_suffix; static const string starters_file_suffix; static const string nodes_file_suffix; static const string edges_file_suffix; static const string xml_file_suffix; static const string json_starters_file_suffix; static const string json_nodes_file_suffix; static const string json_edges_file_suffix; static const string json_file_suffix; bool original; // The extended kmer comes originally from the starter (true), or (false) if is it a degenerated kmer (one substitution or one indel). FILE *graph_file,*nodes_file,*edges_file,*starters_file; GraphOutput(string prefix, int graph_format); GraphOutput(string prefix, int graph_format, id_els first_id_els); //PIERRE GraphOutput(string prefix); GraphOutput(string prefix, id_els first_id_els); //PIERRE void close(); long sequence_length(string line); void print_node(long index, char *ascii_node); void print_edge(long index, long id, long id2, string label); void print_edge(long index, long id, long id2, string label, string comment); void print_starter_head(int index, char* sequence); void print_starter_end(); enum LeftOrRight { LEFT=0, RIGHT=1 }; enum Strand { FW=0, RC=1 }; struct node_strand { long node; Strand strand; LeftOrRight left_or_right; node_strand(long node, Strand strand, LeftOrRight left_or_right) : node(node), strand(strand), left_or_right(left_or_right) {} bool operator<(const node_strand &other) const { if (node != other.node) return (node < other.node); if (left_or_right != other.left_or_right) return left_or_right < other.left_or_right; return (strand < other.strand); } }; std::unordered_map > kmer_links; id_els construct_graph(string linear_seqs_name, const string direction); // PIERRE: added the return value id_els construct_graph(string linear_seqs_name); // PIERRE: added the return value void load_nodes_extremities(string linear_seqs_name); private: void init(bool erase); // PIERRE }; #endif //_GRAPHOUTPUT_H kissplice-2.4.0-p1/debruijn-v4/minia/Hash16.cpp000644 000765 000024 00000023543 12676004527 021124 0ustar00ishistaff000000 000000 // // Hash16.cpp // // Created by Guillaume Rizk on 19/02/12. // #include #include #include #include // for max #include "Hash16.h" using namespace::std; Hash16::Hash16() { //empty default constructor nb_elem = 0; } //tai is 2^tai_Hash16 //max is 32 Hash16::Hash16(int tai_Hash16) { if(tai_Hash16>32){ fprintf(stderr,"max size for this hash is 2^32, resuming with max value \n"); tai_Hash16=32; } nb_elem = 0; tai = (1LL << tai_Hash16); mask = tai-1 ; datah = (cell_ptr_t *) malloc( tai * sizeof(cell_ptr_t)); //create hashtable memset(datah,0, tai * sizeof(cell_ptr_t)); // fprintf(stderr,"sizeof hashtable %lli MB\n",tai * sizeof(cell_ptr_t)/1024/1024); storage = new Pool; } Hash16::~Hash16() { free(datah); delete storage; } //if graine already here, overwrite old value void Hash16::insert(hash_elem graine, int value) { unsigned int clef ; cell * cell_ptr, *newcell_ptr; cell_ptr_t newcell_internal_ptr; clef = (unsigned int) (hashcode(graine) & mask); cell_ptr = storage->internal_ptr_to_cell_pointer(datah[clef]); while(cell_ptr != NULL && cell_ptr->graine != graine) { cell_ptr = storage->internal_ptr_to_cell_pointer(cell_ptr->suiv); } if (cell_ptr==NULL) //graine non trouvee , insertion au debut { newcell_internal_ptr = storage->allocate_cell(); newcell_ptr = storage->internal_ptr_to_cell_pointer(newcell_internal_ptr); newcell_ptr->val=value; newcell_ptr->graine=graine; newcell_ptr->suiv=datah[clef]; datah[clef] = newcell_internal_ptr; nb_elem++; } else cell_ptr->val=value; // graine trouvee } //add graine, and count how many times it was added //return 1 if graine first time seen int Hash16::add(hash_elem graine) { unsigned int clef ; cell * cell_ptr, *newcell_ptr; cell_ptr_t newcell_internal_ptr; clef = (unsigned int) hashcode(graine) & mask; cell_ptr = storage->internal_ptr_to_cell_pointer(datah[clef]); while(cell_ptr != NULL && cell_ptr->graine != graine) { cell_ptr = storage->internal_ptr_to_cell_pointer(cell_ptr->suiv); } if (cell_ptr==NULL) //graine non trouvee , insertion au debut { newcell_internal_ptr = storage->allocate_cell(); newcell_ptr = storage->internal_ptr_to_cell_pointer(newcell_internal_ptr); newcell_ptr->val=1; newcell_ptr->graine=graine; newcell_ptr->suiv=datah[clef]; datah[clef] = newcell_internal_ptr; nb_elem++; return 1; } else { (cell_ptr->val)++; // graine trouvee return 0; } } int Hash16::has_key( hash_elem graine) { return get(graine,NULL); } int Hash16::get( hash_elem graine, int * val) { unsigned int clef ; cell * cell_ptr; clef = (unsigned int) hashcode(graine) & mask; cell_ptr = storage->internal_ptr_to_cell_pointer(datah[clef]); while(cell_ptr != NULL && cell_ptr->graine != graine) { cell_ptr = storage->internal_ptr_to_cell_pointer(cell_ptr->suiv); } if (cell_ptr==NULL) { return 0; } else { if (val != NULL) *val = cell_ptr->val; return 1; } } int Hash16::remove( hash_elem graine, int * val) { unsigned int clef ; cell* cell_ptr; cell_ptr_t * cellprec_ptr; clef = (unsigned int) hashcode(graine) & mask; cell_ptr = storage->internal_ptr_to_cell_pointer(datah[clef]); cellprec_ptr = & (datah[clef]); while(cell_ptr != NULL && cell_ptr->graine != graine) { cellprec_ptr = & (cell_ptr->suiv); cell_ptr = storage->internal_ptr_to_cell_pointer(cell_ptr->suiv); } if (cell_ptr==NULL) { if (val != NULL) *val = 0; return 0; } else { if (val != NULL) *val = cell_ptr->val; //delete the cell : *cellprec_ptr = cell_ptr->suiv ; return 1; } } // (note: Hash16 uses 32 bits hashes) #ifdef _largeint inline uint64_t Hash16::hashcode(LargeInt elem) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits)] uint64_t result = 0, chunk, mask = ~0; LargeInt intermediate = elem; int i; for (i=0;i> 64; result ^= hashcode(chunk); } return result; } #endif #ifdef _ttmath inline uint64_t Hash16::hashcode(ttmath::UInt elem) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits)] uint64_t result = 0, to_hash; ttmath::UInt intermediate = elem; uint32_t mask=~0, chunk; int i; for (i=0;i>= 32; (intermediate & mask).ToInt(chunk); to_hash |= ((uint64_t)chunk) << 32 ; intermediate >>= 32; result ^= hashcode(to_hash); } return result; } #endif #ifdef _LP64 inline unsigned int Hash16::hashcode( __uint128_t elem ) { // hashcode(uint128) = ( hashcode(upper 64 bits) xor hashcode(lower 64 bits)) & mask return (hashcode((uint64_t)(elem>>64)) ^ hashcode((uint64_t)(elem&((((__uint128_t)1)<<64)-1)))); } #endif inline unsigned int Hash16::hashcode( uint64_t elem ) { uint64_t code = elem; code = code ^ (code >> 14); //supp code = (~code) + (code << 18); code = code ^ (code >> 31); code = code * 21; code = code ^ (code >> 11); code = code + (code << 6); code = code ^ (code >> 22); return ((unsigned int) code ); } void Hash16::empty_all() { storage->empty_all(); nb_elem=0; memset(datah,0, tai * sizeof(cell_ptr_t)); } // call start_iterator to reinit the iterator, then do a while(next_iterator()) {..} to traverse every cell void Hash16::start_iterator() { iterator.cell_index = -1; iterator.cell_ptr = NULL; iterator.cell_internal_ptr = 0; } // returns true as long as the iterator contains a valid cell bool Hash16::next_iterator() { while (1) { // if the current cell is empty, search datah for the next non-empty one if (iterator.cell_internal_ptr == 0) { while (iterator.cell_internal_ptr == 0) { iterator.cell_index++; if ((unsigned int )iterator.cell_index==tai) return false; iterator.cell_internal_ptr = datah[iterator.cell_index]; } } else // if the current cell is non-empty, go to the next cell { iterator.cell_internal_ptr = iterator.cell_ptr->suiv; if (iterator.cell_internal_ptr == 0) continue; // if the next cell is empty, proceed to the "current cell is empty" case } // at this point we either gave up (return false) or have a non-empty cell iterator.cell_ptr = storage->internal_ptr_to_cell_pointer(iterator.cell_internal_ptr); break; } return true; } //file should already be opened for writing void Hash16::dump(FILE * count_file) { cell * cell_ptr; start_iterator(); while (next_iterator()) { cell_ptr = iterator.cell_ptr; fwrite(&cell_ptr->graine, sizeof(cell_ptr->graine), 1, count_file); fwrite(&cell_ptr->val, sizeof(cell_ptr->val), 1, count_file); } } int64_t Hash16::getsolids(Bloom* bloom_to_insert, BinaryBank* solids, int nks) { cell * cell_ptr; start_iterator(); int64_t nso=0; while (next_iterator()) { cell_ptr = iterator.cell_ptr; if(cell_ptr->val>=nks) { nso++; solids->write_element(&cell_ptr->graine); if (bloom_to_insert != NULL) bloom_to_insert->add(cell_ptr->graine); } } return nso; } //print stats of elem having their value >=nks int Hash16::printstat(int nks, bool print_collisions) { fprintf(stderr,"\n----------------------Stat Hash Table ---------------------\n"); long long NbKmersolid = 0; int ma=0,mi=99999,cpt=0; uint64_t i; int maxclef=0; cell * cell_ptr; cell_ptr_t cell_internal_ptr; int distrib_colli[512]; long long nb_cell=0; for (i=0;i<512;i++){distrib_colli[i]=0;} for (i=0; iinternal_ptr_to_cell_pointer(cell_internal_ptr); cpt=0; while(cell_internal_ptr!=0 ) { nb_cell++; cpt++; if(cell_ptr->val >= nks) NbKmersolid++; cell_internal_ptr = cell_ptr->suiv; cell_ptr = storage->internal_ptr_to_cell_pointer(cell_internal_ptr); } if(cpt>ma) {ma=cpt;maxclef=i;} ma = max(ma,cpt); mi = min(mi,cpt); distrib_colli[cpt]++; } fprintf(stderr,"taille hashtable %llu\n",(unsigned long long)tai); fprintf(stderr,"kmer solid/total : %lli / %lli %g %% (%lli elem < %i) \n",(long long)NbKmersolid, (long long)nb_cell, 100*(float)NbKmersolid /nb_cell,(long long)(nb_cell-NbKmersolid),nks); fprintf(stderr,"max collisions = %i pour clef %i nb_elem total %lli reparties sur %llu clefs %g elem/clef \n",ma,maxclef,nb_cell,(unsigned long long)(tai-distrib_colli[0]), (float)nb_cell/(float)(tai-distrib_colli[0])); if(print_collisions) for (i=0; i<10; i++) { fprintf(stderr," %9llucollisions : %9i \n",(unsigned long long)i,(distrib_colli[i])); } return NbKmersolid; } kissplice-2.4.0-p1/debruijn-v4/minia/Hash16.h000644 000765 000024 00000003602 12676004527 020563 0ustar00ishistaff000000 000000 // // Hash16.h // // Created by Guillaume Rizk on 19/02/12. // #ifndef Hash16_h #define Hash16_h #include #include #include #include "Pool.h" #include "Bloom.h" #include "Bank.h" //memory usage : sizeof(cell_ptr_t)*tai + sizeof(cell)*Nb_inserted // ie 4B*tai + 16B*nb_inserted #ifdef _largeint #include "LargeInt.h" typedef LargeInt hash_elem; #else #ifdef _ttmath #include "ttmath/ttmath.h" typedef ttmath::UInt hash_elem; #else #if (! defined kmer_type) || (! defined _LP64) typedef uint64_t hash_elem; #else typedef kmer_type hash_elem; #endif #endif #endif class Hash16{ protected: cell_ptr_t * datah; Pool* storage; uint64_t mask ; #ifdef _largeint inline uint64_t hashcode(LargeInt elem); #endif #ifdef _ttmath inline uint64_t hashcode(ttmath::UInt elem); #endif #ifdef _LP64 unsigned int hashcode( __uint128_t elem); #endif unsigned int hashcode( uint64_t elem); public: //print stats of elem having their value >=nks // void printstat(int nks); int printstat(int nks, bool print_collisions=0); void empty_all(); void insert(hash_elem graine, int val); int add(hash_elem elem); void dump(FILE * count_file);//file should already be opened for writing int64_t getsolids( Bloom* bloom_to_insert, BinaryBank* solids, int nks); int get( hash_elem elem, int * val); int has_key( hash_elem elem); int remove( hash_elem graine, int * val); // iterator functions: struct { int64_t cell_index; cell * cell_ptr; cell_ptr_t cell_internal_ptr; } iterator; void start_iterator(); bool next_iterator(); uint64_t tai; uint64_t nb_elem; Hash16(int tai_Hash16); Hash16(); ~Hash16(); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Hashing.cpp000644 000765 000024 00000002146 12676004527 021447 0ustar00ishistaff000000 000000 #include "Hashing.h" #ifdef _largeint // sadly inlining this provokes a linker error.. uint64_t Hashing::hashcode(LargeInt elem) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits)] uint64_t result = 0, chunk, mask = ~0; LargeInt intermediate = elem; int i; for (i=0;i> 64; result ^= hashcode(chunk); } return result; } #endif #ifdef _LP64 uint64_t Hashing::hashcode( __uint128_t elem ) { // hashcode(uint128) = ( hashcode(upper 64 bits) xor hashcode(lower 64 bits)) return (hashcode((uint64_t)(elem>>64)) ^ hashcode((uint64_t)(elem&((((__uint128_t)1)<<64)-1)))); } #endif uint64_t Hashing::hashcode( uint64_t elem ) { // RanHash from Numerical Recipes 3rd edition uint64_t v = elem * 3935559000370003845ULL + 2691343689449507681ULL; v = v ^ (v >> 21); v = v ^ (v << 37); v = v ^ (v >> 4); v = v * 4768777513237032717ULL; v = v ^ (v << 20); v = v ^ (v >> 41); v = v ^ (v << 5); return v; } kissplice-2.4.0-p1/debruijn-v4/minia/Hashing.h000644 000765 000024 00000001027 12676004527 021111 0ustar00ishistaff000000 000000 #ifndef Hashing_h #define Hashing_h #include #include #include #ifdef _largeint #include "LargeInt.h" #else #ifdef _ttmath #include "ttmath/ttmath.h" #endif #endif // hash functions: [ any integer type, e.g. 64 bits, 128 bits or ttmath ] -> [ 64 bits hash ] class Hashing { public: #ifdef _largeint static uint64_t hashcode(LargeInt elem); #endif #ifdef _LP64 static uint64_t hashcode( __uint128_t elem ); #endif static uint64_t hashcode( uint64_t elem ); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Kmer.cpp000644 000765 000024 00000023006 12676004527 020762 0ustar00ishistaff000000 000000 #ifndef ASSERTS #define NDEBUG // disable asserts, they're computationnally intensive #endif #include #include #include // for min #include "Kmer.h" #include "lut.h" using namespace std; int sizeKmer; uint64_t nsolids = 0; kmer_type kmerMask; kmer_type kmerMaskm1; int NT2int(char nt) { int i; i = nt; i = (i>>1)&3; // that's quite clever, guillaume. return i; } int revcomp_int(int nt_int) { return (nt_int<2)?nt_int+2:nt_int-2; } unsigned char code4NT(char *seq) { int i; unsigned char x; x=0; for (i=0; i<4; ++i) { x = x*4 + NT2int(seq[i]); } return x; } unsigned char code_n_NT(char *seq, int nb) { int i; unsigned char x; x=0; for (i=0; i> 2) + ( ((kmer_type) comp_NT[NT2int(seq[sizeKmer-1])]) << (2*(sizeKmer-1)) ) ) & kmerMask; } // warning: only call this function for sequential enumeration of kmers (no arbitrary position) kmer_type extractKmerFromRead(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp) { return extractKmerFromRead(readSeq, position, graine, graine_revcomp, true); } kmer_type extractKmerFromRead(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp, bool sequential) { return extractKmerFromRead(readSeq, position, graine, graine_revcomp, sequential, sizeKmer, kmerMask); } kmer_type extractKmerFromRead(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp, bool sequential, int sizeKmer, kmer_type kmerMask) { assert(graine != graine_revcomp); // make sure two different pointers bool new_read = (position == 0) || (!sequential); // faster computation for immediately overlapping kmers *graine = codeSeedRight(&readSeq[position], *graine, new_read, sizeKmer, kmerMask); *graine_revcomp = codeSeedRight_revcomp(&readSeq[position], *graine_revcomp, new_read, sizeKmer, kmerMask); return min(*graine,*graine_revcomp); } int first_nucleotide(kmer_type kmer) { int result; #ifdef _largeint LargeInt t = kmer; result = t.toInt()&3; #else #ifdef _ttmath ttmath::UInt t = kmer&3; t.ToInt(result); #else result = kmer&3; #endif #endif return result; } int code2seq (kmer_type code, char *seq) { return code2seq (code, seq, sizeKmer, kmerMask); } int code2seq (kmer_type code, char *seq, int sizeKmer, kmer_type kmerMask) { int i; kmer_type temp = code; char bin2NT[4] = {'A','C','T','G'}; for (i=sizeKmer-1; i>=0; i--) { seq[i]=bin2NT[first_nucleotide(temp&3)]; temp = temp>>2; } //printf("sizeKmer = %d\n", sizeKmer); seq[sizeKmer]='\0'; return sizeKmer; } // return the i-th nucleotide of the kmer_type kmer int code2nucleotide( kmer_type code, int which_nucleotide) { kmer_type temp = code; temp = temp >> (2*(sizeKmer-1-which_nucleotide)); return first_nucleotide(temp&3); } uint64_t revcomp(uint64_t x, int size) { int i; uint64_t revcomp = x; // printf("x %x revcomp %x \n",x,revcomp); unsigned char * kmerrev = (unsigned char *) (&revcomp); unsigned char * kmer = (unsigned char *) (&x); for (i=0; i<8; ++i) { kmerrev[7-i] = revcomp_4NT[kmer[i]]; } return (revcomp >> (2*( 4*sizeof(uint64_t) - size)) ) ; } uint64_t revcomp(uint64_t x) { return revcomp(x,sizeKmer); } #ifdef _largeint LargeInt revcomp(LargeInt x, int size) { int i; kmer_type revcomp = x; // printf("x %x revcomp %x \n",x,revcomp); unsigned char * kmerrev = (unsigned char *) (&(revcomp.array[0])); unsigned char * kmer = (unsigned char *) (&(x.array[0])); for (i=0; i<8*KMER_PRECISION; ++i) { kmerrev[8*KMER_PRECISION-1-i] = revcomp_4NT[kmer[i]]; } return (revcomp >> (2*( 32*KMER_PRECISION - size)) ) ; } LargeInt revcomp(LargeInt x) { return revcomp(x,sizeKmer); } #endif #ifdef _ttmath ttmath::UInt revcomp(ttmath::UInt x, int size) { int i; kmer_type revcomp = x; // printf("x %x revcomp %x \n",x,revcomp); unsigned char * kmerrev = (unsigned char *) (&revcomp); unsigned char * kmer = (unsigned char *) (&x); for (i=0; i<4*KMER_PRECISION; ++i) { kmerrev[4*KMER_PRECISION-1-i] = revcomp_4NT[kmer[i]]; } return (revcomp >> (2*( 16*KMER_PRECISION - size)) ) ; } ttmath::UInt revcomp(ttmath::UInt x) { return revcomp(x,sizeKmer); } #endif #ifdef _LP64 __uint128_t revcomp(__uint128_t x, int size) { // ---64bits-- ---64bits-- // original kmer: [__high_nucl__|__low_nucl___] // // ex: [ AC | .......TG ] // //revcomp: [ CA | .......GT ] // \_low_nucl__/\high_nucl/ uint64_t high_nucl = (uint64_t)(x>>64); int nb_high_nucl = size>32?size - 32:0; __uint128_t revcomp_high_nucl = revcomp(high_nucl, nb_high_nucl); if (size<=32) revcomp_high_nucl = 0; // srsly dunno why this is needed. gcc bug? uint64_t x ---> (x>>64) != 0 uint64_t low_nucl = (uint64_t)(x&((((__uint128_t)1)<<64)-1)); int nb_low_nucl = size>32?32:size; __uint128_t revcomp_low_nucl = revcomp(low_nucl, nb_low_nucl); return (revcomp_low_nucl<<(2*nb_high_nucl)) + revcomp_high_nucl; } __uint128_t revcomp(__uint128_t x) { return revcomp(x,sizeKmer); } #endif // will be used by assemble() void revcomp_sequence(char s[], int len) { #define CHAR_REVCOMP(a,b) {switch(a){\ case 'A': b='T';break;case 'C': b='G';break;case 'G': b='C';break;case 'T': b='A';break;default: b=a;break;}} int i; unsigned char t; for (i=0;i> 2 ) + ( ((kmer_type)added_nt) << ((sizeKmer-1)*2)) ) & kmerMask; // previous kmer kmer_type revcomp_new_graine = revcomp(new_graine); if (strand != NULL) *strand = (new_graine < revcomp_new_graine)?0:1; return min(new_graine,revcomp_new_graine); } //////////////////////////funcs for binary reads kmer_type codeSeed_bin(char *seq) { int i; kmer_type x; x=0; for (i=0; i> 2) + ( ((kmer_type) comp_NT[(int)(seq[sizeKmer-1])]) << (2*(sizeKmer-1)) ) ) & kmerMask; } kmer_type extractKmerFromRead_bin(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp, bool use_compressed) { assert(graine != graine_revcomp); // make sure two different pointers bool new_read = (position == 0); if(!use_compressed) { *graine = codeSeedRight(&readSeq[position], *graine, new_read); *graine_revcomp = codeSeedRight_revcomp(&readSeq[position], *graine_revcomp, new_read); } else { *graine = codeSeedRight_bin(&readSeq[position], *graine, new_read); *graine_revcomp = codeSeedRight_revcomp_bin(&readSeq[position], *graine_revcomp, new_read); } return min(*graine,*graine_revcomp); } // debug only: convert a kmer_type to char* char debug_kmer_buffer[1024]; char* print_kmer(kmer_type kmer) { return print_kmer(kmer,sizeKmer,kmerMask); } char* print_kmer(kmer_type kmer, int sizeKmer, kmer_type kmerMask) { code2seq(kmer,debug_kmer_buffer, sizeKmer, kmerMask); return debug_kmer_buffer; } kissplice-2.4.0-p1/debruijn-v4/minia/Kmer.h000644 000765 000024 00000006034 12676004527 020431 0ustar00ishistaff000000 000000 #ifndef Kmer64_h #define Kmer64_h #include #ifdef _largeint #include "LargeInt.h" typedef LargeInt kmer_type; #else #ifdef _ttmath #include "ttmath/ttmath.h" typedef ttmath::UInt kmer_type; #else #if (! defined kmer_type) || (! defined _LP64) typedef uint64_t kmer_type; #endif #endif #endif extern int sizeKmer; extern kmer_type kmerMask; extern kmer_type kmerMaskm1; extern uint64_t nsolids; int NT2int(char nt); int revcomp_int(int nt_int); kmer_type codeSeed(char *seq, int sizeKmer, kmer_type kmerMask); kmer_type codeSeed(char *seq); kmer_type codeSeedRight(char *seq, kmer_type val_seed, bool new_read); kmer_type codeSeedRight(char *seq, kmer_type val_seed, bool new_read, int sizeKmer, kmer_type kmerMask); kmer_type codeSeedRight_revcomp(char *seq, kmer_type val_seed, bool new_read); kmer_type codeSeedRight_revcomp(char *seq, kmer_type val_seed, bool new_read, int sizeKmer, kmer_type kmerMask); unsigned char code_n_NT(char *seq, int nb); unsigned char code4NT(char *seq); uint64_t revcomp(uint64_t x); uint64_t revcomp(uint64_t x, int size); #ifdef _largeint LargeInt revcomp(LargeInt x); LargeInt revcomp(LargeInt x, int size); #endif #ifdef _ttmath ttmath::UInt revcomp(ttmath::UInt x); ttmath::UInt revcomp(ttmath::UInt x, int size); #endif #ifdef _LP64 __uint128_t revcomp(__uint128_t x); __uint128_t revcomp(__uint128_t x, int size); #endif int code2seq ( kmer_type code,char *seq); int code2seq ( kmer_type code,char *seq, int sizeKmer, kmer_type kmerMask); int code2nucleotide( kmer_type code, int which_nucleotide); kmer_type extractKmerFromRead(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp); kmer_type extractKmerFromRead(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp, bool sequential); kmer_type extractKmerFromRead(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp, bool sequential, int sizeKmer, kmer_type kmerMask); // compute the next kmer w.r.t forward or reverse strand, e.g. for ACTG (revcomp = CAGT) // it makes sure the result is the min(kmer,revcomp_kmer) // indicates if the result is the revcomp_kmer by setting *strand // examples: // next_kmer(ACTG,A,&0)=CTGA with strand = 0 (because revcomp=TCAG); // next_kmer(ACTG,A,&1)= (revcomp of ACTG + A = CAGT+A = ) AGTA with strand = 0 (because revcomp = TACT) kmer_type next_kmer(kmer_type graine, int added_nt, int *strand); void revcomp_sequence(char s[], int len); kmer_type codeSeed_bin(char *seq); kmer_type codeSeedRight_bin(char *seq, kmer_type val_seed, bool new_read); kmer_type codeSeedRight_revcomp_bin(char *seq, kmer_type val_seed, bool new_read); kmer_type extractKmerFromRead_bin(char *readSeq, int position, kmer_type *graine, kmer_type *graine_revcomp, bool use_compressed); char* print_kmer(kmer_type kmer); // debugging char* print_kmer(kmer_type kmer, int sizeKmer, kmer_type kmerMask); // debugging #endif kissplice-2.4.0-p1/debruijn-v4/minia/LargeInt.cpp000644 000765 000024 00000017327 12676004527 021602 0ustar00ishistaff000000 000000 #ifndef ASSERTS #define NDEBUG // disable asserts; those asserts make sure that with PRECISION == [1 or 2], all is correct #endif // some 64-bit assert macros #if defined(_LP64) && defined(_largeint) #define assert128(x) assert(precision != 2 || (x)); #else #define assert128(x) ; #endif #include #include #include "LargeInt.h" using namespace std; template LargeInt::LargeInt() { } template LargeInt::LargeInt(const uint64_t &c) { array[0] = c; for (int i = 1; i < precision; i++) array[i] = 0; } template LargeInt LargeInt::operator+ (const LargeInt& other) const { LargeInt result; int carry = 0; for (int i = 0 ; i < precision ; i++) { result.array[i] = array[i] + other.array[i] + carry; carry = (result.array[i] < array[i]) ? 1 : 0; } assert(precision != 1 || (result == other.array[0] + array[0])); assert128(result.toInt128() == other.toInt128() + toInt128()); return result; } template LargeInt LargeInt::operator- (const LargeInt& other) const { LargeInt result; int carry = 0; for (int i = 0 ; i < precision ; i++) { result.array[i] = array[i] - other.array[i] - carry; carry = (result.array[i] > array[i]) ? 1 : 0; } assert(precision != 1 || (result == array[0] - other.array[0])); assert128(result.toInt128() == toInt128() - other.toInt128()); return result; } template LargeInt LargeInt::operator* (const int& coeff) const { LargeInt result (*this); // minia doesn't have that many multiplications cases if (coeff == 2 || coeff == 4) { result = result << (coeff / 2); } else { if (coeff == 21) { result = (result << 4) + (result << 2) + result; } else { printf("unsupported LargeInt multiplication: %d\n",coeff); exit(1); } } assert(precision != 1 || (result == array[0] * coeff)); assert128(result.toInt128() == toInt128() * coeff); return result; } template LargeInt LargeInt::operator/ (const uint32_t& divisor) const { LargeInt result; fill( result.array, result.array + precision, 0 ); // inspired by Divide32() from http://subversion.assembla.com/svn/pxcode/RakNet/Source/BigInt.cpp uint64_t r = 0; uint32_t mask32bits = ~0; for (int i = precision-1; i >= 0; --i) { for (int j = 1; j >= 0; --j) // [j=1: high-32 bits, j=0: low-32 bits] of array[i] { uint64_t n = (r << 32) | ((array[i] >> (32*j)) & mask32bits ); result.array[i] = result.array[i] | (((n / divisor) & mask32bits) << (32*j)); r = n % divisor; } } assert(precision != 1 || (result == array[0] / divisor)); assert128(result.toInt128() == toInt128() / divisor); return result; } template uint32_t LargeInt::operator% (const uint32_t& divisor) const { uint64_t r = 0; uint32_t mask32bits = ~0; for (int i = precision-1; i >= 0; --i) { for (int j = 1; j >= 0; --j) // [j=1: high-32 bits, j=0: low-32 bits] of array[i] { uint64_t n = (r << 32) | ((array[i] >> (32*j)) & mask32bits ); r = n % divisor; } } assert(precision != 1 || (r == array[0] % divisor)); assert128(r == toInt128() % divisor); return (uint32_t)r; } template LargeInt LargeInt::operator^ (const LargeInt& other) const { LargeInt result; for (int i=0 ; i < precision ; i++) result.array[i] = array[i] ^ other.array[i]; assert(precision != 1 || (result == (array[0] ^ other.array[0]))); assert128(result.toInt128() == (toInt128() ^ other.toInt128())); return result; } template LargeInt LargeInt::operator& (const LargeInt& other) const { LargeInt result; for (int i=0 ; i < precision ; i++) result.array[i] = array[i] & other.array[i]; assert(precision != 1 || (result == (array[0] & other.array[0]))); assert128(result.toInt128() == (toInt128() & other.toInt128())); return result; } template LargeInt LargeInt::operator~ () const { LargeInt result; for (int i=0 ; i < precision ; i++) result.array[i] = ~array[i]; assert(precision != 1 || (result == ~array[0])); assert128(result.toInt128() == ~toInt128()); return result; } template LargeInt LargeInt::operator<< (const int& coeff) const { LargeInt result (0); int large_shift = coeff / 64; int small_shift = coeff % 64; for (int i = large_shift ; i < precision-1; i++) { result.array[i] = result.array[i] | (array[i-large_shift] << small_shift); if (small_shift == 0) // gcc "bug".. uint64_t x; x>>64 == 1<<63, x<<64 == 1 result.array[i+1] = 0; else result.array[i+1] = array[i-large_shift] >> (64 - small_shift); } result.array[precision-1] = result.array[precision-1] | (array[precision-1-large_shift] << small_shift); assert(precision != 1 || (result == (array[0] << coeff))); assert128(result.toInt128() == (toInt128() << coeff)); return result; } template LargeInt LargeInt::operator>> (const int& coeff) const { LargeInt result (0); int large_shift = coeff / 64; int small_shift = coeff % 64; result.array[0] = (array[large_shift] >> small_shift); for (int i = 1 ; i < precision - large_shift ; i++) { result.array[i] = (array[i+large_shift] >> small_shift); if (small_shift == 0 && large_shift > 0) // gcc "bug".. uint64_t x; x>>64 == 1<<63, x<<64 == 1 { result.array[i-1] = result.array[i-1]; } else { result.array[i-1] = result.array[i-1] | (array[i+large_shift] << (64 - small_shift)); } } assert(precision != 1 || ( small_shift == 0 || (result == array[0] >> coeff))); assert128(small_shift == 0 || (result.toInt128() == (toInt128() >> coeff))); return result; } template bool LargeInt::operator!= (const LargeInt& c) const { for (int i = 0 ; i < precision ; i++) if( array[i] != c.array[i] ) return true; return false; } template bool LargeInt::operator== (const LargeInt& c) const { for (int i = 0 ; i < precision ; i++) if( array[i] != c.array[i] ) return false; return true; } template bool LargeInt::operator< (const LargeInt& c) const { for (int i = precision-1 ; i>=0 ; --i) if( array[i] != c.array[i] ) return array[i] < c.array[i]; return false; } template bool LargeInt::operator<=(const LargeInt& c) const { return operator==(c) || operator<(c); } template uint64_t LargeInt::toInt() const { return array[0]; } #ifdef _LP64 template __uint128_t LargeInt::toInt128() const { return ((__uint128_t)array[0]) + (((__uint128_t)array[1]) << ((__uint128_t)64)); } #endif #ifdef KMER_PRECISION template class LargeInt; // since we didn't define the functions in a .h file, that trick removes linker errors, see http://www.parashift.com/c++-faq-lite/separate-template-class-defn-from-decl.html #endif kissplice-2.4.0-p1/debruijn-v4/minia/LargeInt.h000644 000765 000024 00000002345 12676004527 021241 0ustar00ishistaff000000 000000 /* * arbitrary-precision integer library * very limited: only does what minia needs (but not what minia deserves) */ #ifndef LargeInt_h #define LargeInt_h #include #include template class LargeInt { public: uint64_t array[precision]; LargeInt(const uint64_t &); LargeInt(); // overloading LargeInt operator+(const LargeInt &) const; LargeInt operator-(const LargeInt &) const; LargeInt operator*(const int &) const; LargeInt operator/(const uint32_t &) const; uint32_t operator%(const uint32_t &) const; LargeInt operator^(const LargeInt &) const; LargeInt operator&(const LargeInt &) const; LargeInt operator~() const; LargeInt operator<<(const int &) const; LargeInt operator>>(const int &) const; bool operator!=(const LargeInt &) const; bool operator==(const LargeInt &) const; bool operator<(const LargeInt &) const; bool operator<=(const LargeInt &) const; // custom uint64_t toInt() const; #ifdef _LP64 __uint128_t toInt128() const; #endif // c++ fun fact: // "const" will ban the function from being anything which can attempt to alter any member variables in the object. }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/LICENSE000644 000765 000024 00000054331 12676004527 020372 0ustar00ishistaff000000 000000 FREE SOFTWARE LICENSING AGREEMENT CeCILL ======================================== Notice ------ This Agreement is a free software license that is the result of discussions between its authors in order to ensure compliance with the two main principles guiding its drafting: - firstly, its conformity with French law, both as regards the law of torts and intellectual property law, and the protection that it offers to authors and the holders of economic rights over software. - secondly, compliance with the principles for the distribution of free software: access to source codes, extended user-rights. The following bodies are the authors of this license CeCILL (Ce : CEA, C : CNRS, I : INRIA, LL : Logiciel Libre): Commissariat à l'Energie Atomique - CEA, a public scientific, technical and industrial establishment, having its principal place of business at 31-33 rue de la Fédération, 75752 PARIS cedex 15, France. Centre National de la Recherche Scientifique - CNRS, a public scientific and technological establishment, having its principal place of business at 3 rue Michel-Ange 75794 Paris cedex 16, France. Institut National de Recherche en Informatique et en Automatique - INRIA, a public scientific and technological establishment, having its principal place of business at Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex. PREAMBLE -------- The purpose of this Free Software Licensing Agreement is to grant users the right to modify and redistribute the software governed by this license within the framework of an "open source" distribution model. The exercising of these rights is conditional upon certain obligations for users so as to ensure that this status is retained for subsequent redistribution operations. As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors only have limited liability. In this respect, it is brought to the user's attention that the risks associated with loading, using, modifying and/or developing or reproducing the software by the user given its nature of Free Software, that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the Software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured and, more generally, to use and operate it in the same conditions of security. This Agreement may be freely reproduced and published, provided it is not altered, and that no Articles are either added or removed herefrom. This Agreement may apply to any or all software for which the holder of the economic rights decides to submit the operation thereof to its provisions. Article 1 - DEFINITIONS ------------------------ For the purposes of this Agreement, when the following expressions commence with a capital letter, they shall have the following meaning: Agreement: means this Licensing Agreement, and any or all of its subsequent versions. Software: means the software in its Object Code and/or Source Code form and, where applicable, its documentation, "as is" at the time when the Licensee accepts the Agreement. Initial Software: means the Software in its Source Code and/or Object Code form and, where applicable, its documentation, "as is" at the time when it is distributed for the first time under the terms and conditions of the Agreement. Modified Software: means the Software modified by at least one Contribution. Source Code: means all the Software's instructions and program lines to which access is required so as to modify the Software. Object Code: means the binary files originating from the compilation of the Source Code. Holder: means the holder of the economic rights over the Initial Software. Licensee(s): mean(s) the Software user(s) having accepted the Agreement. Contributor: means a Licensee having made at least one Contribution. Licensor: means the Holder, or any or all other individual or legal entity, that distributes the Software under the Agreement. Contributions: mean any or all modifications, corrections, translations, adaptations and/or new functionalities integrated into the Software by any or all Contributor, and the Static Modules. Module: means a set of sources files including their documentation that, once compiled in executable form, enables supplementary functionalities or services to be developed in addition to those offered by the Software. Dynamic Module: means any or all module, created by the Contributor, that is independent of the Software, so that this module and the Software are in two different executable forms that are run in separate address spaces, with one calling the other when they are run. Static Module: means any or all module, created by the Contributor and connected to the Software by a static link that makes their object codes interdependent. This module and the Software to which it is connected, are combined in a single executable. Parties: mean both the Licensee and the Licensor. These expressions may be used both in singular and plural form. Article 2 - PURPOSE ------------------- The purpose of the Agreement is to enable the Licensor to grant the Licensee a free, non-exclusive, transferable and worldwide License for the Software as set forth in Article 5 hereinafter for the whole term of protection of the rights over said Software. Article 3 - ACCEPTANCE ---------------------- 3.1. The Licensee shall be deemed as having accepted the terms and conditions of this Agreement by the occurrence of the first of the following events: - (i) loading the Software by any or all means, notably, by downloading from a remote server, or by loading from a physical medium; - (ii) the first time the Licensee exercises any of the rights granted hereunder. 3.2. One copy of the Agreement, containing a notice relating to the specific nature of the Software, to the limited warranty, and to the limitation to use by experienced users has been provided to the Licensee prior to its acceptance as set forth in Article 3.1 hereinabove, and the Licensee hereby acknowledges that it is aware thereof. Article 4 - EFFECTIVE DATE AND TERM ----------------------------------- 4.1. EFFECTIVE DATE The Agreement shall become effective on the date when it is accepted by the Licensee as set forth in Article 3.1. 4.2. TERM The Agreement shall remain in force during the whole legal term of protection of the economic rights over the Software. Article 5 - SCOPE OF THE RIGHTS GRANTED --------------------------------------- The Licensor hereby grants to the Licensee, that accepts such, the following rights as regards the Software for any or all use, and for the term of the Agreement, on the basis of the terms and conditions set forth hereinafter. Otherwise, the Licensor grants to the Licensee free of charge exploitation rights on the patents he holds on whole or part of the inventions implemented in the Software. 5.1. RIGHTS OF USE The Licensee is authorized to use the Software, unrestrictedly, as regards the fields of application, with it being hereinafter specified that this relates to: - permanent or temporary reproduction of all or part of the Software by any or all means and in any or all form. - loading, displaying, running, or storing the Software on any or all medium. - entitlement to observe, study or test the operation thereof so as to establish the ideas and principles that form the basis for any or all constituent elements of said Software. This shall apply when the Licensee carries out any or all loading, displaying, running, transmission or storage operation as regards the Software, that it is entitled to carry out hereunder. 5.2. entitlement to make CONTRIBUTIONS The right to make Contributions includes the right to translate, adapt, arrange, or make any or all modification to the Software, and the right to reproduce the resulting Software. The Licensee is authorized to make any or all Contribution to the Software provided that it explicitly mentions its name as the author of said Contribution and the date of the development thereof. 5.3. DISTRIBUTION AND PUBLICATION RIGHTS In particular, the right of distribution and publication includes the right to transmit and communicate the Software to the general public on any or all medium, and by any or all means, and the right to market, either in consideration of a fee, or free of charge, a copy or copies of the Software by means of any or all process. The Licensee is further authorized to redistribute copies of the modified or unmodified Software to third parties according to the terms and conditions set forth hereinafter. 5.3.1. REDISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION The Licensee is authorized to redistribute true copies of the Software in Source Code or Object Code form, provided that said redistribution complies with all the provisions of the Agreement and is accompanied by: - a copy of the Agreement, - a notice relating to the limitation of both the Licensor's warranty and liability as set forth in Articles 8 and 9, and that, in the event that only the Software's Object Code is redistributed, the Licensee allows future Licensees unhindered access to the Software's full Source Code by providing them with the terms and conditions for access thereto, it being understood that the additional cost of acquiring the Source Code shall not exceed the cost of transferring the data. 5.3.2. REDISTRIBUTION OF MODIFIED SOFTWARE When the Licensee makes a Contribution to the Software, the terms and conditions for the redistribution of the Modified Software shall then be subject to all the provisions hereof. The Licensee is authorized to redistribute the Modified Software, in Source Code or Object Code form, provided that said redistribution complies with all the provisions of the Agreement and is accompanied by: - a copy of the Agreement, - a notice relating to the limitation of both the Licensor's warranty and liability as set forth in Articles 8 and 9, and that, in the event that only the Modified Software's Object Code is redistributed, the Licensee allows future Licensees unhindered access to the Modified Software's full Source Code by providing them with the terms and conditions for access thereto, it being understood that the additional cost of acquiring the Source Code shall not exceed the cost of transferring the data. 5.3.3. redistribution OF DYNAMIC MODULES When the Licensee has developed a Dynamic Module, the terms and conditions hereof do not apply to said Dynamic Module, that may be distributed under a separate Licensing Agreement. 5.3.4. COMPATIBILITY WITH THE GPL LICENSE In the event that the Modified or unmodified Software is included in a code that is subject to the provisions of the GPL License, the Licensee is authorized to redistribute the whole under the GPL License. In the event that the Modified Software includes a code that is subject to the provisions of the GPL License, the Licensee is authorized to redistribute the Modified Software under the GPL License. Article 6 - INTELLECTUAL PROPERTY ---------------------------------- 6.1. OVER THE INITIAL SOFTWARE The Holder owns the economic rights over the Initial Software. Any or all use of the Initial Software is subject to compliance with the terms and conditions under which the Holder has elected to distribute its work and no one shall be entitled to and it shall have sole entitlement to modify the terms and conditions for the distribution of said Initial Software. The Holder undertakes to maintain the distribution of the Initial Software under the conditions of the Agreement, for the duration set forth in article 4.2.. 6.2. OVER THE CONTRIBUTIONS The intellectual property rights over the Contributions belong to the holder of the economic rights as designated by effective legislation. 6.3. OVER THE DYNAMIC MODULES The Licensee having developed a Dynamic Module is the holder of the intellectual property rights over said Dynamic Module and is free to choose the agreement that shall govern its distribution. 6.4. JOINT PROVISIONS 6.4.1. The Licensee expressly undertakes: - not to remove, or modify, in any or all manner, the intellectual property notices affixed to the Software; - to reproduce said notices, in an identical manner, in the copies of the Software. 6.4.2. The Licensee undertakes not to directly or indirectly infringe the intellectual property rights of the Holder and/or Contributors and to take, where applicable, vis-à-vis its staff, any or all measures required to ensure respect for said intellectual property rights of the Holder and/or Contributors. Article 7 - RELATED SERVICES ----------------------------- 7.1. Under no circumstances shall the Agreement oblige the Licensor to provide technical assistance or maintenance services for the Software. However, the Licensor is entitled to offer this type of service. The terms and conditions of such technical assistance, and/or such maintenance, shall then be set forth in a separate instrument. Only the Licensor offering said maintenance and/or technical assistance services shall incur liability therefor. 7.2. Similarly, any or all Licensor shall be entitled to offer to its Licensees, under its own responsibility, a warranty, that shall only be binding upon itself, for the redistribution of the Software and/or the Modified Software, under terms and conditions that it shall decide upon itself. Said warranty, and the financial terms and conditions of its application, shall be subject to a separate instrument executed between the Licensor and the Licensee. Article 8 - LIABILITY ---------------------- 8.1. Subject to the provisions of Article 8.2, should the Licensor fail to fulfill all or part of its obligations hereunder, the Licensee shall be entitled to claim compensation for the direct loss suffered as a result of a fault on the part of the Licensor, subject to providing evidence of it. 8.2. The Licensor's liability is limited to the commitments made under this Licensing Agreement and shall not be incurred as a result , in particular: (i) of loss due the Licensee's total or partial failure to fulfill its obligations, (ii) direct or consequential loss due to the Software's use or performance that is suffered by the Licensee, when the latter is a professional using said Software for professional purposes and (iii) consequential loss due to the Software's use or performance. The Parties expressly agree that any or all pecuniary or business loss (i.e. loss of data, loss of profits, operating loss, loss of customers or orders, opportunity cost, any disturbance to business activities) or any or all legal proceedings instituted against the Licensee by a third party, shall constitute consequential loss and shall not provide entitlement to any or all compensation from the Licensor. Article 9 - WARRANTY --------------------- 9.1. The Licensee acknowledges that the current situation as regards scientific and technical know-how at the time when the Software was distributed did not enable all possible uses to be tested and verified, nor for the presence of any or all faults to be detected. In this respect, the Licensee's attention has been drawn to the risks associated with loading, using, modifying and/or developing and reproducing the Software that are reserved for experienced users. The Licensee shall be responsible for verifying, by any or all means, the product's suitability for its requirements, its due and proper functioning, and for ensuring that it shall not cause damage to either persons or property. 9.2. The Licensor hereby represents, in good faith, that it is entitled to grant all the rights on the Software (including in particular the rights set forth in Article 5 hereof over the Software). 9.3. The Licensee acknowledges that the Software is supplied "as is" by the Licensor without any or all other express or tacit warranty, other than that provided for in Article 9.2 and, in particular, without any or all warranty as to its market value, its secured, innovative or relevant nature. Specifically, the Licensor does not warrant that the Software is free from any or all error, that it shall operate continuously, that it shall be compatible with the Licensee's own equipment and its software configuration, nor that it shall meet the Licensee's requirements. 9.4. The Licensor does not either expressly or tacitly warrant that the Software does not infringe any or all third party intellectual right relating to a patent, software or to any or all other property right. Moreover, the Licensor shall not hold the Licensee harmless against any or all proceedings for infringement that may be instituted in respect of the use, modification and redistribution of the Software. Nevertheless, should such proceedings be instituted against the Licensee, the Licensor shall provide it with technical and legal assistance for its defense. Such technical and legal assistance shall be decided upon on a case-by-case basis between the relevant Licensor and the Licensee pursuant to a memorandum of understanding. The Licensor disclaims any or all liability as regards the Licensee's use of the Software's name. No warranty shall be provided as regards the existence of prior rights over the name of the Software and as regards the existence of a trademark. Article 10 - TERMINATION ------------------------- 10.1. In the event of a breach by the Licensee of its obligations hereunder, the Licensor may automatically terminate this Agreement thirty (30) days after notice has been sent to the Licensee and has remained ineffective. 10.2. The Licensee whose Agreement is terminated shall no longer be authorized to use, modify or distribute the Software. However, any or all licenses that it may have granted prior to termination of the Agreement shall remain valid subject to their having been granted in compliance with the terms and conditions hereof. Article 11 - MISCELLANEOUS PROVISIONS -------------------------------------- 11.1. EXCUSABLE EVENTS Neither Party shall be liable for any or all delay, or failure to perform the Agreement, that may be attributable to an event of force majeure, an act of God or an outside cause, such as, notably, defective functioning, or interruptions affecting the electricity or telecommunications networks, blocking of the network following a virus attack, the intervention of the government authorities, natural disasters, water damage, earthquakes, fire, explosions, strikes and labor unrest, war, etc. 11.2. The fact that either Party may fail, on one or several occasions, to invoke one or several of the provisions hereof, shall under no circumstances be interpreted as being a waiver by the interested Party of its entitlement to invoke said provision(s) subsequently. 11.3. The Agreement cancels and replaces any or all previous agreement, whether written or oral, between the Parties and having the same purpose, and constitutes the entirety of the agreement between said Parties concerning said purpose. No supplement or modification to the terms and conditions hereof shall be effective as regards the Parties unless it is made in writing and signed by their duly authorized representatives. 11.4. In the event that one or several of the provisions hereof were to conflict with a current or future applicable act or legislative text, said act or legislative text shall take precedence, and the Parties shall make the necessary amendments so as to be in compliance with said act or legislative text. All the other provisions shall remain effective. Similarly, the fact that a provision of the Agreement may be null and void, for any reason whatsoever, shall not cause the Agreement as a whole to be null and void. 11.5. LANGUAGE The Agreement is drafted in both French and English. In the event of a conflict as regards construction, the French version shall be deemed authentic. Article 12 - NEW VERSIONS OF THE AGREEMENT ------------------------------------------- 12.1. Any or all person is authorized to duplicate and distribute copies of this Agreement. 12.2. So as to ensure coherence, the wording of this Agreement is protected and may only be modified by the authors of the License, that reserve the right to periodically publish updates or new versions of the Agreement, each with a separate number. These subsequent versions may address new issues encountered by Free Software. 12.3. Any or all Software distributed under a given version of the Agreement may only be subsequently distributed under the same version of the Agreement, or a subsequent version, subject to the provisions of article 5.3.4. Article 13 - GOVERNING LAW AND JURISDICTION ------------------------------------------- 13.1. The Agreement is governed by French law. The Parties agree to endeavor to settle the disagreements or disputes that may arise during the performance of the Agreement out-of-court. 13.2. In the absence of an out-of-court settlement within two (2) months as from their occurrence, and unless emergency proceedings are necessary, the disagreements or disputes shall be referred to the Paris Courts having jurisdiction, by the first Party to take action. Version 1.1 of 10/26/2004 kissplice-2.4.0-p1/debruijn-v4/minia/LinearCounter.cpp000644 000765 000024 00000002601 12676004527 022634 0ustar00ishistaff000000 000000 #include // for max #include "LinearCounter.h" using namespace std; // for max // counter the number of distinct kmers // implements a linear counter following [1] K. Whang, B. T. Vander-Zaden, H.M. Taylor. A Liner-Time Probabilistic Counting Algorithm for Database Applications // an easier presentation is there: http://highlyscalable.wordpress.com/2012/05/01/probabilistic-structures-web-analytics-data-mining/ // here, it's implements as a wrapper around a special Bloom LinearCounter::LinearCounter(long size) : desired_size(size) { int bloom_nbits = max( (int)ceilf(log2f(size)), 1); bloom = new Bloom(bloom_nbits); bloom->set_number_of_hash_func(1); bloom_size = 1L << bloom_nbits; } void LinearCounter::add(bloom_elem kmer) { bloom->add(kmer); } int LinearCounter::contains(bloom_elem kmer) { // dummy, because bloom_pass_reads wants this method to be exposed return 0; } long LinearCounter::count() { long weight = bloom->weight(); //printf("linear counter load factor: %0.2f\n",(1.0*weight/bloom_size)); return (long) ( (-1.0*bloom_size) * logf( (1.0*bloom_size - weight) / bloom_size ) ); // linear counter cardinality estimation } bool LinearCounter::is_accurate() { long weight = bloom->weight(); float load_factor = (1.0*weight/bloom_size); return load_factor < 0.99; } LinearCounter::~LinearCounter() { delete bloom; } kissplice-2.4.0-p1/debruijn-v4/minia/LinearCounter.h000644 000765 000024 00000001042 12676004527 022277 0ustar00ishistaff000000 000000 // // LinearCounter.h #ifndef LinearCounter_h #define LinearCounter_h #include #include #include #include // for log2f #include "Bank.h" #include "Bloom.h" class LinearCounter{ protected: Bloom *bloom; unsigned long desired_size, bloom_size; public: void add(bloom_elem kmer); long count(); int contains(bloom_elem kmer); // dummy, because bloom_pass_reads wants this method to be exposed bool is_accurate(); LinearCounter(long size); ~LinearCounter(); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/lut.h000644 000765 000024 00000003762 12676004527 020344 0ustar00ishistaff000000 000000 #ifndef CODE_H #define CODE_H //look up table conversion (with A,C,T,G <--> 0,1,2,3) //complement of one NT const unsigned char comp_NT[4] = { 2,3,0,1 }; //reverse complement of 4NT, ie one byte const unsigned char revcomp_4NT[256] = { 0xaa, 0xea, 0x2a, 0x6a, 0xba, 0xfa, 0x3a, 0x7a, 0x8a, 0xca, 0xa, 0x4a, 0x9a, 0xda, 0x1a, 0x5a, 0xae, 0xee, 0x2e, 0x6e, 0xbe, 0xfe, 0x3e, 0x7e, 0x8e, 0xce, 0xe, 0x4e, 0x9e, 0xde, 0x1e, 0x5e, 0xa2, 0xe2, 0x22, 0x62, 0xb2, 0xf2, 0x32, 0x72, 0x82, 0xc2, 0x2, 0x42, 0x92, 0xd2, 0x12, 0x52, 0xa6, 0xe6, 0x26, 0x66, 0xb6, 0xf6, 0x36, 0x76, 0x86, 0xc6, 0x6, 0x46, 0x96, 0xd6, 0x16, 0x56, 0xab, 0xeb, 0x2b, 0x6b, 0xbb, 0xfb, 0x3b, 0x7b, 0x8b, 0xcb, 0xb, 0x4b, 0x9b, 0xdb, 0x1b, 0x5b, 0xaf, 0xef, 0x2f, 0x6f, 0xbf, 0xff, 0x3f, 0x7f, 0x8f, 0xcf, 0xf, 0x4f, 0x9f, 0xdf, 0x1f, 0x5f, 0xa3, 0xe3, 0x23, 0x63, 0xb3, 0xf3, 0x33, 0x73, 0x83, 0xc3, 0x3, 0x43, 0x93, 0xd3, 0x13, 0x53, 0xa7, 0xe7, 0x27, 0x67, 0xb7, 0xf7, 0x37, 0x77, 0x87, 0xc7, 0x7, 0x47, 0x97, 0xd7, 0x17, 0x57, 0xa8, 0xe8, 0x28, 0x68, 0xb8, 0xf8, 0x38, 0x78, 0x88, 0xc8, 0x8, 0x48, 0x98, 0xd8, 0x18, 0x58, 0xac, 0xec, 0x2c, 0x6c, 0xbc, 0xfc, 0x3c, 0x7c, 0x8c, 0xcc, 0xc, 0x4c, 0x9c, 0xdc, 0x1c, 0x5c, 0xa0, 0xe0, 0x20, 0x60, 0xb0, 0xf0, 0x30, 0x70, 0x80, 0xc0, 0x0, 0x40, 0x90, 0xd0, 0x10, 0x50, 0xa4, 0xe4, 0x24, 0x64, 0xb4, 0xf4, 0x34, 0x74, 0x84, 0xc4, 0x4, 0x44, 0x94, 0xd4, 0x14, 0x54, 0xa9, 0xe9, 0x29, 0x69, 0xb9, 0xf9, 0x39, 0x79, 0x89, 0xc9, 0x9, 0x49, 0x99, 0xd9, 0x19, 0x59, 0xad, 0xed, 0x2d, 0x6d, 0xbd, 0xfd, 0x3d, 0x7d, 0x8d, 0xcd, 0xd, 0x4d, 0x9d, 0xdd, 0x1d, 0x5d, 0xa1, 0xe1, 0x21, 0x61, 0xb1, 0xf1, 0x31, 0x71, 0x81, 0xc1, 0x1, 0x41, 0x91, 0xd1, 0x11, 0x51, 0xa5, 0xe5, 0x25, 0x65, 0xb5, 0xf5, 0x35, 0x75, 0x85, 0xc5, 0x5, 0x45, 0x95, 0xd5, 0x15, 0x55 }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Minia.cpp000644 000765 000024 00000027767 12676004527 021143 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include #include #include // for max/min #include // for sorting_kmers #include #define NNKS 4 // default minimal abundance for solidity #define MIN_CONTIG_SIZE (2*sizeKmer+1) int max_memory; // the most memory one should alloc at any time, in MB int order = 0; // deblooming order; 0 = debloom everything; 1 = don't debloom 1-node tips (experimental, untested, shouldn't work);// (made extern int in Traversal.h) #include "Bank.h" #include "Hash16.h" #include "Set.h" #include "Pool.h" #include "Bloom.h" #include "Debloom.h" #include "Utils.h" #include "SortingCount.h" #include "Terminator.h" #include "Kmer.h" #include "Traversal.h" #include "rvalues.h" // for 4bloom int64_t genome_size; Bloom * bloo1; inline void assemble() { //////------------------------------------------------------------------------------------------- fprintf (stderr,"______________________________________________________ \n"); fprintf (stderr,"___________ Assemble from bloom filter _______________ \n"); fprintf (stderr,"______________________________________________________ \n\n"); //////------------------------------------------------------------------------------------------- long long len_left = 0; long long len_right = 0; long long contig_len =0; long long maxlen=10000000; char *left_traversal = (char *) malloc(maxlen*sizeof(char)); char *right_traversal = (char *) malloc(maxlen*sizeof(char)); char *contig = (char *) malloc(2*(maxlen+sizeKmer)*sizeof(char)); kmer_type kmer; long long nbContig =0; long long nbSmallContig =0; long long totalnt=0; long long max_contig_len=0; long long mlenleft=0,mlenright=0; int64_t NbBranchingKmer=0; char kmer_seq[sizeKmer+1]; FILE * file_assembly = fopen(return_file_name(assembly_file),"w+"); BinaryBank *SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer_type),0); STARTWALL(assembly); char *assemble_only_one_region = NULL; // debugging, set to a ASCII kmer to activate, NULL to desactivate bool LOAD_BRANCHING_KMERS=false; // debugging bool DUMP_BRANCHING_KMERS=false; BranchingTerminator *terminator; if (LOAD_BRANCHING_KMERS) { BinaryBank *BranchingKmers = new BinaryBank(return_file_name(branching_kmers_file),sizeof(kmer_type),false); terminator = new BranchingTerminator(BranchingKmers,SolidKmers, bloo1,false_positives); BranchingKmers->close(); } else terminator = new BranchingTerminator(SolidKmers,genome_size, bloo1,false_positives); if (DUMP_BRANCHING_KMERS) { BinaryBank *BranchingKmers = new BinaryBank(return_file_name(branching_kmers_file),sizeof(kmer_type),true); terminator->dump_branching_kmers(BranchingKmers); BranchingKmers->close(); } #ifdef UNITIG SimplePathsTraversal *traversal = new SimplePathsTraversal(bloo1,false_positives,terminator); fprintf (stderr,"_________________Assembling in Unitig mode ..._____________________ \n\n"); #else MonumentTraversal *traversal = new MonumentTraversal(bloo1,false_positives,terminator); #endif //RandomBranchingTraversal *traversal = new RandomBranchingTraversal(bloo1,false_positives,terminator); traversal->set_maxlen(maxlen); traversal->set_max_depth(500); traversal->set_max_breadth(20); while (terminator->next(&kmer)) { // keep looping while a starting kmer is available from this kmer // everything will be marked during the traversal()'s kmer_type starting_kmer; #ifdef UNITIG while (traversal->get_new_starting_node_improved(kmer,starting_kmer)) #else while (traversal->find_starting_kmer(kmer,starting_kmer)) #endif { code2seq(starting_kmer,kmer_seq); // convert starting kmer to nucleotide seq traversal->revert_stats(); // set stats from the last commit (discard stats from find_starting_kmer / small contigs) if (assemble_only_one_region != NULL) { kmer_type dummy; starting_kmer = extractKmerFromRead(assemble_only_one_region,0,&kmer,&dummy,false); } // right extension len_right = traversal->traverse(starting_kmer,right_traversal,0); mlenright= max(len_right,mlenright); // left extension, is equivalent to right extension of the revcomp len_left = traversal->traverse(starting_kmer,left_traversal,1); mlenleft= max(len_left,mlenleft); // form the contig revcomp_sequence(left_traversal,len_left); strcpy(contig,left_traversal); // contig = revcomp(left_traversal) strcat(contig,kmer_seq);// + starting_kmer strcat(contig,right_traversal);// + right_traversal contig_len=len_left+len_right+sizeKmer; // save the contig if(contig_len >= MIN_CONTIG_SIZE) { max_contig_len = max(max_contig_len,contig_len); fprintf(file_assembly,">%lli__len__%lli \n",nbContig,contig_len); fprintf(file_assembly,"%s\n",contig); nbContig++; totalnt+=contig_len; traversal->commit_stats(); } else { traversal->revert_stats(); nbSmallContig++; } if (assemble_only_one_region != NULL) break; } NbBranchingKmer++; if ((NbBranchingKmer%300)==0) fprintf (stderr,"%cLooping through branching kmer n° %lld / %lld total nt %lld ",13,(long long int) NbBranchingKmer,(long long int) terminator->nb_branching_kmers, (long long int)totalnt ); if (nbContig > 0 && assemble_only_one_region != NULL) break; } fclose(file_assembly); fprintf (stderr,"\n Total nt assembled %lli nbContig %lli\n",totalnt,nbContig); fprintf (stderr," Max contig len %lli (debug: max len left %lli, max len right %lli)\n",max_contig_len,mlenleft,mlenright); fprintf (stderr,"\n Debug traversal stats: %ld ends of contigs (%lld unsaved small contigs), among them:\n",traversal->final_stats.ended_traversals,nbSmallContig); fprintf (stderr," %ld couldn't validate consensuses\n",traversal->final_stats.couldnt_validate_consensuses); fprintf (stderr," %ld large bubble breadth, %ld large bubble depth, %ld marked kmer, %ld no extension\n",traversal->final_stats.couldnt_traverse_bubble_breadth,traversal->final_stats.couldnt_traverse_bubble_depth,traversal->final_stats.couldnt_because_marked_kmer,traversal->final_stats.couldnt_find_extension); fprintf (stderr," %ld in-branchin large depth, %ld in-branching large breadth, %ld in-branching other\n",traversal->final_stats.couldnt_inbranching_depth,traversal->final_stats.couldnt_inbranching_breadth,traversal->final_stats.couldnt_inbranching_other); STOPWALL(assembly,"Assembly"); free(left_traversal); free(right_traversal); free(contig); SolidKmers->close(); } int main(int argc, char *argv[]) { if(argc < 6) { fprintf (stderr,"usage:\n"); fprintf (stderr," %s input_file kmer_size min_abundance estimated_genome_size prefix\n",argv[0]); fprintf (stderr,"hints:\n min_abundance ~ 3\n estimated_genome_size is in bp, does not need to be accurate, only controls memory usage\n prefix is any name you want the results to start with\n"); return 1; } bool FOUR_BLOOM_VERSION = true; // shortcuts to go directly to assembly using serialized bloom and serialized hash int START_FROM_SOLID_KMERS=0; // if = 0, construct the fasta file of solid kmers, if = 1, start directly from that file int LOAD_FALSE_POSITIVE_KMERS=0; // if = 0, construct the fasta file of false positive kmers (debloom), if = 1, load that file into the hashtable int NO_FALSE_POSITIVES_AT_ALL=0; // if = 0, normal behavior, if = 1, don't load false positives (will be a probabilistic de bruijn graph) int max_disk_space = 0;// let dsk decide for (int n_a = 6; n_a < argc ; n_a++) { if (strcmp(argv[n_a],"--original") == 0) FOUR_BLOOM_VERSION = false; if (strcmp(argv[n_a],"--dont-count")==0) START_FROM_SOLID_KMERS = 1; if (strcmp(argv[n_a],"--dont-debloom")==0) LOAD_FALSE_POSITIVE_KMERS = 1; if (strcmp(argv[n_a],"--just-assemble")==0) { START_FROM_SOLID_KMERS = 1; LOAD_FALSE_POSITIVE_KMERS = 1; } if (strcmp(argv[n_a],"--titus-mode")==0) NO_FALSE_POSITIVES_AT_ALL = 1; if (strcmp(argv[n_a],"-d")==0) max_disk_space = atoi(argv[n_a+1]); if (strcmp(argv[n_a],"-maxc")==0) max_couv = atoi(argv[n_a+1]); if (strcmp(argv[n_a],"--le-changement")==0) {printf("c'est maintenant!\n");exit(0);} } // kmer size sizeKmer=27; // let's make it even for now, because i havnt thought of how to handle palindromes (dont want to stop on them) if(argc >= 3) { sizeKmer = atoi(argv[2]); if (sizeKmer%2==0) { sizeKmer-=1; printf("Need odd kmer size to avoid palindromes. I've set kmer size to %d.\n",sizeKmer); } if (sizeKmer>((int)sizeof(kmer_type)*4)) { printf("Max kmer size on this compiled version is %lu\n",sizeof(kmer_type)*4); exit(1); } } if (sizeKmer == (int)(sizeof(kmer_type)*4)) kmerMask = -1; else kmerMask=(((kmer_type)1)<<(sizeKmer*2))-1; double lg2 = log(2); if (sizeKmer > 128) { FOUR_BLOOM_VERSION = false; printf("Reverted to single Bloom filter implementation for k>128\n"); } if (!FOUR_BLOOM_VERSION) NBITS_PER_KMER = log(16*sizeKmer*(lg2*lg2))/(lg2*lg2); // needed to process argv[5] else NBITS_PER_KMER = rvalues[sizeKmer][1]; // solidity nks =NNKS; if(argc >= 4) { nks = atoi(argv[3]); if (nks==0) nks=1; // min abundance can't be 0 } if(argc >= 5) { genome_size = atoll(argv[4]); // int estimated_bloom_size = max( (int)ceilf(log2f(genome_size * NBITS_PER_KMER )), 1); uint64_t estimated_bloom_size = (uint64_t) (genome_size * NBITS_PER_KMER); uint64_t estimated_nb_FP = (uint64_t)(genome_size * 4 * powf(0.6,11)); // just indicative //max_memory = max( (1LL << estimated_bloom_size)/8LL /1024LL/1024LL, 1LL ); max_memory = max((int64_t) estimated_bloom_size/8LL /1024LL/1024LL,1LL); printf("estimated values: nbits Bloom %lli, nb FP %lld, max memory %i MB\n",estimated_bloom_size,estimated_nb_FP,max_memory); } // output prefix if(argc >= 6) { strcpy(prefix,argv[5]); } fprintf (stderr,"taille cell %lu \n", sizeof(cell)); STARTWALL(0); Bank *Reads = new Bank(argv[1]); // counter kmers, write solid kmers to disk if (!START_FROM_SOLID_KMERS) { int verbose = 0; bool write_count = false; bool skip_binary_conversion = false; sorting_count(Reads,prefix,max_memory,max_disk_space,write_count,verbose, skip_binary_conversion); } // debloom, write false positives to disk, insert them into false_positives if (! LOAD_FALSE_POSITIVE_KMERS) { debloom(order, max_memory); } bloo1 = bloom_create_bloo1((BloomCpt *)NULL, false); if (! NO_FALSE_POSITIVES_AT_ALL) { // load false positives from disk into false_positives if (!FOUR_BLOOM_VERSION) false_positives = load_false_positives(); else false_positives = load_false_positives_cascading4(); } else { // titus mode: no FP's false_positives = dummy_false_positives(); } // return 1; assemble(); STOPWALL(0,"Total"); delete Reads; return 0; } kissplice-2.4.0-p1/debruijn-v4/minia/MultiConsumer.cpp000644 000765 000024 00000006110 12676004527 022667 0ustar00ishistaff000000 000000 #include "MultiConsumer.h" template MultiConsumer::MultiConsumer() : sizeBuffer(0), maxSizeBuffer(NB_PACKETS), allDone(false) { mutex = new pthread_mutex_t; mutex2 = new pthread_mutex_t; pthread_mutex_init(mutex, NULL); pthread_mutex_init(mutex2, NULL); notFull = new pthread_cond_t; notEmpty = new pthread_cond_t; pthread_cond_init(notFull, NULL); pthread_cond_init(notEmpty, NULL); } template void MultiConsumer::produce(Task *task) { pthread_mutex_lock(mutex); while (sizeBuffer == maxSizeBuffer) { pthread_cond_wait(notFull, mutex); } buffer[sizeBuffer++] = task; pthread_cond_signal(notEmpty); pthread_mutex_unlock(mutex); } template Task *MultiConsumer::consume() { Task *task; pthread_mutex_lock(mutex); while (sizeBuffer == 0) { if (isAllDone()) { pthread_mutex_unlock(mutex); return NULL; } pthread_cond_wait(notEmpty, mutex); } task = buffer[--sizeBuffer]; pthread_cond_signal(notFull); pthread_mutex_unlock(mutex); return task; } template void MultiConsumer::setAllDone() { pthread_mutex_lock(mutex2); allDone = true; pthread_cond_signal(notEmpty); pthread_mutex_unlock(mutex2); } template bool MultiConsumer::isAllDone() { bool res; pthread_mutex_lock(mutex2); res = allDone; pthread_mutex_unlock(mutex2); return res; } // wrapper around MultiConsumer to bundle 10 MB of reads together MultiReads::MultiReads(int thread_id) : thread_id(thread_id) { mc = new MultiConsumer(); new_packet(); } void MultiReads::new_packet() { current_packet = new reads_packet; nbRead = 0; nbNucleotides = 0; } void MultiReads::produce(char *read, int readlen) { if (nbRead > 0 && nbNucleotides > MAX_NUCL_IN_PACKET) { mc->produce(current_packet); //printf("%d - produced (%d in stack)\n", thread_id, mc->sizeBuffer); new_packet(); } current_packet->reads.push_back(read); nbRead++; nbNucleotides += readlen; } void MultiReads::setAllDone() { if (nbRead > 0) // produce that last packet { mc->produce(current_packet); } mc->setAllDone(); } bool MultiReadsConsumer::is_finished_packet() { return nbRead == current_packet->reads.size(); } void MultiReadsConsumer::consume(char* &read, int &readlen) { if (!has_packet || is_finished_packet()) { if (current_packet != NULL) { delete current_packet; } //printf("%d - attempting to consume (%d remaining)\n", thread_id, mc->sizeBuffer); current_packet = mc->consume(); //printf("%d - consumed (%d remaining)\n", thread_id, mc->sizeBuffer); nbRead = 0; has_packet = true; if (current_packet == NULL) // no more reads, all done { readlen = 0; return; } } readlen = current_packet->reads[nbRead].length(); read = (char *)current_packet->reads[nbRead].c_str(); nbRead++; } kissplice-2.4.0-p1/debruijn-v4/minia/MultiConsumer.h000644 000765 000024 00000002731 12676004527 022341 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #ifndef MultiConsumer_H #define MultiConsumer_H using namespace std; #define NB_PACKETS 5 #define MAX_NUCL_IN_PACKET 10000000 template class MultiConsumer { Task *buffer[NB_PACKETS]; int maxSizeBuffer; pthread_mutex_t *mutex; pthread_mutex_t *mutex2; pthread_cond_t *notFull; pthread_cond_t *notEmpty; bool allDone; public: int sizeBuffer; MultiConsumer(); void produce(Task *task); Task *consume(); void setAllDone(); bool isAllDone(); }; // a producer that group reads struct reads_packet { vector reads; }; class MultiReads { protected: reads_packet *current_packet; int packetOffset; int nbRead; int nbNucleotides; int thread_id; void new_packet(); public: MultiConsumer *mc; MultiReads(int thread_id); void produce(char *read, int readlen); void setAllDone(); }; // a consumer for the above producer class MultiReadsConsumer { reads_packet *current_packet; int packetOffset; int nbRead; int thread_id; bool has_packet; MultiConsumer *mc; public: MultiReadsConsumer(MultiReads m, int thread_id) : has_packet(false), mc(m.mc), current_packet(NULL), thread_id(thread_id) {} bool is_finished_packet(); void consume(char* &read, int &readlen); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/OAHash.cpp000644 000765 000024 00000012074 12676004527 021172 0ustar00ishistaff000000 000000 // open-addressing hash table with linear probing, follows wikipedia // to reduce memory, elements with [value == 0] are UNOCCUPIED, deal with it. #include #include #include #include // for max #include "OAHash.h" using namespace::std; OAHash::OAHash(uint64_t max_memory) // in bytes { hash_size = max_memory / sizeof(element_pair); if (hash_size == 0) { printf("empty OAHash allocated\n"); exit(1); } nb_inserted_keys = 0; data = (element_pair *) calloc( hash_size, sizeof(element_pair)); //create hashtable } OAHash::~OAHash() { free(data); } int OAHash::size_entry() { return sizeof(element_pair); } // hash functions: [ any integer type, e.g. 64 bits, 128 bits or ttmath ] -> [ 64 bits hash ] #ifdef _largeint inline uint64_t OAHash::hashcode(LargeInt elem) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits)] uint64_t result = 0, chunk, mask = ~0; LargeInt intermediate = elem; int i; for (i=0;i> 64; result ^= hashcode(chunk); } return result; } #endif #ifdef _ttmath inline uint64_t OAHash::hashcode(ttmath::UInt elem) { // hash = XOR_of_series[hash(i-th chunk iof 64 bits) uint64_t result = 0, to_hash; ttmath::UInt intermediate = elem; uint32_t cmask=~0, chunk; int i; for (i=0;i>= 32; (intermediate & cmask).ToInt(chunk); to_hash |= ((uint64_t)chunk) << 32 ; intermediate >>= 32; result ^= hashcode(to_hash); } return result; } #endif #ifdef _LP64 inline uint64_t OAHash::hashcode( __uint128_t elem ) { // hashcode(uint128) = ( hashcode(upper 64 bits) xor hashcode(lower 64 bits)) return (hashcode((uint64_t)(elem>>64)) ^ hashcode((uint64_t)(elem&((((__uint128_t)1)<<64)-1)))); } #endif inline uint64_t OAHash::hashcode( uint64_t elem ) { uint64_t code = elem; code = code ^ (code >> 14); //supp code = (~code) + (code << 18); code = code ^ (code >> 31); code = code * 21; code = code ^ (code >> 11); code = code + (code << 6); code = code ^ (code >> 22); return code; } bool OAHash::is_occupied(element_pair *element) { return (element->value != 0); } OAHash::element_pair * OAHash::find_slot(key_type key) { uint64_t ptr = hashcode(key) % hash_size; element_pair *element = data+ptr; uint64_t retries = 0; // search until we either find the key, or find an empty slot. while ( ( is_occupied(element)) && ( element->key != key ) && (retries < hash_size)) { ptr = (ptr + 1) % hash_size; element = data+ptr; retries++; } if (retries == hash_size) { printf("OAHash: max rehashes reached: %lld (notify a developer)\n",(long long)hash_size); exit(1); } return element; } //if graine already here, overwrite old value void OAHash::insert(key_type graine, int value) { element_pair *element = find_slot(graine); if (!is_occupied(element)) { element->key = graine; nb_inserted_keys++; } element->value = value; } // increment the value of a graine void OAHash::increment(key_type graine) { element_pair *element = find_slot(graine); if (!is_occupied(element)) { element->key = graine; nb_inserted_keys++; } if( element->value == -1) element->value = 0; //special case, emulate 0 value with -1, (0 is not a valid value, used for empty cell) element->value = element->value + 1; } bool OAHash::get( key_type graine, int * val) { element_pair *element = find_slot(graine); if (!is_occupied(element)) return false; if ((element->key) == graine && (val != NULL)) *val = element->value; if( element->value ==-1) *val = 0; // 0 is emulated with -1 return true; } bool OAHash::has_key(key_type graine) { return get(graine,NULL); } // call start_iterator to reinit the iterator, then do a while(next_iterator()) {..} to traverse every cell void OAHash::start_iterator() { iterator = data-1; } // returns true as long as the iterator contains a valid cell bool OAHash::next_iterator() { while (1) { iterator++; if (iterator == data+hash_size) return false; if (iterator->value != 0) break; } return true; } float OAHash::load_factor() { return (float)nb_inserted_keys/(float)hash_size; } uint64_t OAHash::memory_usage() { return hash_size* sizeof(element_pair); // in bits } void OAHash::printstat() { fprintf(stderr,"\n----------------------Stat OA Hash Table ---------------------\n"); fprintf(stderr,"max elements: %lld, memory usage: %lld\n",(long long)hash_size,(long long)memory_usage()); fprintf(stderr,"load factor: %.2f\n",load_factor()); } kissplice-2.4.0-p1/debruijn-v4/minia/OAHash.h000644 000765 000024 00000002657 12676004527 020645 0ustar00ishistaff000000 000000 #ifndef OAHash_h #define OAHash_h #include #include #include #ifdef _largeint #include "LargeInt.h" typedef LargeInt key_type; #else #ifdef _ttmath #include "ttmath/ttmath.h" typedef ttmath::UInt key_type; #else #if (! defined kmer_type) || (! defined _LP64) typedef uint64_t key_type; #else typedef kmer_type key_type; #endif #endif #endif class OAHash{ protected: struct element_pair { key_type key; //uint32_t value; int32_t value; }; uint64_t hash_size; uint64_t nb_inserted_keys; element_pair* data; #ifdef _largeint inline uint64_t hashcode(LargeInt elem); #endif #ifdef _ttmath inline uint64_t hashcode(ttmath::UInt elem); #endif #ifdef _LP64 uint64_t hashcode( __uint128_t elem); #endif uint64_t hashcode( uint64_t elem); bool is_occupied(element_pair *element); public: static int size_entry(); // iterator functions: element_pair *iterator; void start_iterator(); bool next_iterator(); OAHash(uint64_t max_elements); ~OAHash(); element_pair * find_slot(key_type key); void insert(key_type graine, int value); void increment(key_type graine); bool get( key_type graine, int * val); bool has_key(key_type graine); void printstat(); uint64_t memory_usage(); float load_factor(); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Pool.cpp000644 000765 000024 00000006555 12676004527 021007 0ustar00ishistaff000000 000000 // // Pool.cpp // memory pool for hashtable to avoid mallocs // // Created by Guillaume Rizk on 24/11/11. // #include #include #include "Pool.h" #include /** * Constructeur par dÈfaut */ template Pool::Pool() { n_pools = 0; n_cells=0; //allocation table de pool : tab_pool = (cell**) malloc(N_POOL*sizeof(cell *) ); tab_pool[0]=NULL;n_pools++; // la premiere pool est NULL, pour conversion null_internal -> null //allocation de la premiere pool : pool_courante =(cell*) malloc(TAI_POOL*sizeof(cell) ); tab_pool[n_pools] = pool_courante; n_pools++; } /** * Destructeur */ template Pool::~Pool() { unsigned int i; for(i=1;i void Pool::empty_all() { unsigned int i; for(i=2;i cell * Pool::internal_ptr_to_cell_pointer(cell_ptr_t internal_ptr) { unsigned int numpool = internal_ptr & 1023; unsigned int numcell = internal_ptr >> 10; return (tab_pool[numpool] + numcell); } template cell_ptr_t Pool::allocate_cell() { cell_ptr_t internal_adress = 0; // ncells = nb de cells deja utilisees if (n_cells = N_POOL) { fprintf(stderr,"Internal memory allocator is full!\n"); return 0; // will happen when 4G cells are allocated, representing 64 Go } pool_courante =(cell*) malloc(TAI_POOL*sizeof(cell) ); tab_pool[n_pools] = pool_courante; n_pools++; n_cells = 1; internal_adress = n_pools -1; // low 8 bits : pool number // 22 high bits are 0 return internal_adress; } } // trick to avoid linker errors: http://www.parashift.com/c++-faq-lite/templates.html#faq-35.15 template class Pool; #ifdef _LP64 template class Pool<__uint128_t>; #endif #ifdef _ttmath template class Pool >; #endif #ifdef _largeint template class Pool >; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Pool.h000644 000765 000024 00000003133 12676004527 020441 0ustar00ishistaff000000 000000 // // Pool.h // memory pool for hashtable to avoid mallocs // // Created by Guillaume Rizk on 24/11/11. // #ifndef compress_Pool_h #define compress_Pool_h #include #include // for max/min #ifdef _ttmath #include "ttmath/ttmath.h" #endif #ifdef _largeint #include "LargeInt.h" #endif typedef unsigned int cell_ptr_t; template struct cell { graine_type graine; cell_ptr_t suiv; int val; }; #define TAI_POOL 4194304 //16777216//4194304 // 2^22 16 M cells *16 o blocs de 256 Mo #define N_POOL 1024 //256//1024 // 2^10 soit 4 G cells max /** * \class Pool, * \brief Cette class dÈfinit une pool memoire pour allocation rapide de la table de hachage utilisee quand seed >14 */ template class Pool{ public: /** * table de cell, pour usage courant, */ cell * pool_courante; /** * stockage de tous les pointeurs pool */ cell ** tab_pool; /** * nombre de piscines remplies */ unsigned int n_pools; /** * niveau de remplissage de la piscine courante */ unsigned int n_cells; /** * Constructeur par dÈfaut */ Pool(); /** * alloue une cellule dans la piscine */ cell * allocate_cell_in_pool(); // allocate cell, return internal pointer type ( 32bits) cell_ptr_t allocate_cell(); cell * internal_ptr_to_cell_pointer(cell_ptr_t internal_ptr); /** * vide toutes piscines * (garde juste une pool vide) */ void empty_all(); /** * Destructeur */ ~Pool(); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/rvalues.h000644 000765 000024 00000005732 12676004527 021220 0ustar00ishistaff000000 000000 static const double rvalues[129][2] = { { 0.00000, 5.29625 }, { 0.00000, 0.00000 }, { 0.00000, 0.00000 }, { 0.00000, 0.00000 }, { 0.00000, 0.00000 }, { 5.64856, 5.49117 }, { 5.85164, 5.52333 }, { 6.02772, 5.55385 }, { 6.18398, 5.58291 }, { 6.32492, 5.61065 }, { 6.45362, 5.63721 }, { 6.57225, 5.66268 }, { 6.68244, 5.68717 }, { 6.78544, 5.71075 }, { 6.88221, 5.73350 }, { 6.97353, 5.75548 }, { 7.06004, 5.77674 }, { 7.14227, 5.79733 }, { 7.22066, 5.81730 }, { 7.29558, 5.83669 }, { 7.36734, 5.85553 }, { 7.43623, 5.87386 }, { 7.50248, 5.89171 }, { 7.56630, 5.90910 }, { 7.62788, 5.92605 }, { 7.68737, 5.94260 }, { 7.74494, 5.95876 }, { 7.80069, 5.97455 }, { 7.85477, 5.98999 }, { 7.90726, 6.00510 }, { 7.95826, 6.01989 }, { 8.00786, 6.03437 }, { 8.05615, 6.04856 }, { 8.10319, 6.06247 }, { 8.14904, 6.07611 }, { 8.19378, 6.08950 }, { 8.23745, 6.10264 }, { 8.28012, 6.11555 }, { 8.32181, 6.12822 }, { 8.36260, 6.14068 }, { 8.40250, 6.15293 }, { 8.44157, 6.16497 }, { 8.47983, 6.17682 }, { 8.51733, 6.18848 }, { 8.55409, 6.19995 }, { 8.59015, 6.21125 }, { 8.62553, 6.22238 }, { 8.66025, 6.23334 }, { 8.69435, 6.24413 }, { 8.72784, 6.25478 }, { 8.76075, 6.26527 }, { 8.79310, 6.27562 }, { 8.82490, 6.28582 }, { 8.85619, 6.29589 }, { 8.88697, 6.30582 }, { 8.91725, 6.31562 }, { 8.94707, 6.32530 }, { 8.97643, 6.33485 }, { 9.00534, 6.34428 }, { 9.03383, 6.35360 }, { 9.06189, 6.36280 }, { 9.08956, 6.37189 }, { 9.11683, 6.38088 }, { 9.14372, 6.38975 }, { 9.17024, 6.39853 }, { 9.19640, 6.40721 }, { 9.22221, 6.41578 }, { 9.24768, 6.42427 }, { 9.27282, 6.43265 }, { 9.29764, 6.44095 }, { 9.32214, 6.44916 }, { 9.34634, 6.45728 }, { 9.37024, 6.46532 }, { 9.39385, 6.47328 }, { 9.41717, 6.48115 }, { 9.44023, 6.48894 }, { 9.46301, 6.49666 }, { 9.48552, 6.50430 }, { 9.50778, 6.51186 }, { 9.52979, 6.51935 }, { 9.55156, 6.52677 }, { 9.57308, 6.53412 }, { 9.59437, 6.54140 }, { 9.61543, 6.54861 }, { 9.63627, 6.55576 }, { 9.65688, 6.56284 }, { 9.67729, 6.56986 }, { 9.69748, 6.57682 }, { 9.71747, 6.58371 }, { 9.73725, 6.59055 }, { 9.75684, 6.59732 }, { 9.77624, 6.60404 }, { 9.79544, 6.61070 }, { 9.81446, 6.61731 }, { 9.83330, 6.62386 }, { 9.85196, 6.63035 }, { 9.87045, 6.63680 }, { 9.88876, 6.64319 }, { 9.90691, 6.64953 }, { 9.92489, 6.65582 }, { 9.94271, 6.66206 }, { 9.96037, 6.66825 }, { 9.97787, 6.67439 }, { 9.99522, 6.68049 }, { 10.01241, 6.68654 }, { 10.02946, 6.69254 }, { 10.04637, 6.69850 }, { 10.06313, 6.70441 }, { 10.07975, 6.71029 }, { 10.09623, 6.71611 }, { 10.11258, 6.72190 }, { 10.12879, 6.72764 }, { 10.14488, 6.73335 }, { 10.16083, 6.73901 }, { 10.17665, 6.74463 }, { 10.19235, 6.75022 }, { 10.20793, 6.75577 }, { 10.22339, 6.76127 }, { 10.23873, 6.76674 }, { 10.25394, 6.77218 }, { 10.26905, 6.77757 }, { 10.28404, 6.78293 }, { 10.29892, 6.78826 }, { 10.31369, 6.79355 }, { 10.32835, 6.79881 }, { 10.34290, 6.80403 }, { 10.35735, 6.80922 }, { 10.37169, 6.81437 }, { 10.38593, 6.81950 } }; kissplice-2.4.0-p1/debruijn-v4/minia/Set.cpp000644 000765 000024 00000014176 12676004527 020627 0ustar00ishistaff000000 000000 #include "Set.h" int ListSet::bits_per_element = sizeof(set_elem)*8; ListSet::ListSet(uint64_t taille_approx) { liste.reserve(taille_approx); } ListSet::ListSet() { } void ListSet::insert(set_elem elem) { liste.push_back(elem); } void ListSet::finalize() { sort(liste.begin(), liste.end()); } bool ListSet::contains(set_elem elem) { return binary_search(liste.begin(), liste.end(), elem); } //Raluca bool ListSet::containsNotBinary(set_elem elem) { int i; for (i=0; i)*8; HashSet::HashSet(uint64_t taille_approx) { int NBITS_HT = max( (int)ceilf(log2f(taille_approx)) , 1); hash = new Hash16(NBITS_HT); } void HashSet::insert(set_elem elem) { hash->insert(elem,1); // dummy value } void HashSet::finalize() { // NOP } bool HashSet::contains(set_elem elem) { return hash->has_key(elem); } //-------------- // emulates a hash table with two lists // benefits: lower memory usage // but logarithmic acess instead of constant void AssocSet::finalize() { sort(liste.begin(), liste.end()); liste_value.assign(liste.size(),0); } int AssocSet::get( set_elem elem, set_value_t * val) { vector::iterator it; it = lower_bound(liste.begin(), liste.end(),elem); if (it == liste.end() || elem != *it) return 0; size_t rank = it - liste.begin(); *val = liste_value[rank]; return 1; } AssocSet::AssocSet() { } //return -1 if elem is not in the set int AssocSet::set(set_elem elem, set_value_t val) { vector::iterator it; it = lower_bound(liste.begin(), liste.end(),elem); if (it == liste.end() ||elem != *it) return 0; size_t rank = it - liste.begin(); liste_value[rank]=val; return 1; } void AssocSet::start_iterator() { iterator = liste.begin()-1; } bool AssocSet::next_iterator() { iterator++; if (iterator==liste.end()) return false; return true; } void AssocSet::print_total_size() { printf("Assoc set size: %li\n",liste.size()); printf("Assoc set capacity: listekmer %li liste val%li\n",liste.capacity(),liste_value.capacity()); printf("%li *%li + %li* %li = %li MB \n",liste.capacity(),sizeof(set_elem),liste_value.capacity(),sizeof(set_value_t), (liste.capacity()*sizeof(set_elem)+liste_value.capacity()*sizeof(set_value_t))/1024/1024 ); } void AssocSet::clear() { liste_value.assign(liste_value.size(),0); // liste_value.clear(); } //Raluca //-------------- // emulates a hash table with two lists for paired reads (elements=branching kmers in the second read file, values=pairs of corresponding kmers in the first read file + next nucleotide in the second read file) void copy_nt_kmer (nt_kmer_t from, nt_kmer_t* to){ to->nt = from.nt; to->prev_kmer = from.prev_kmer; } void copy_pair_nt_kmer (pair_nt_kmer_t from, pair_nt_kmer_t* to){ copy_nt_kmer(from.nk1, &(to->nk1)); copy_nt_kmer(from.nk2, &(to->nk2)); } void AssocPairedSet::finalize() { sort(liste.begin(), liste.end()); //liste_value.assign(liste.size(),0); liste_value.reserve(liste.size()); // printf("finalize %i elems \n",liste_value.size()); liste_value.resize(liste.size()); int i; for (i=0; i::iterator it; it = lower_bound(liste.begin(), liste.end(),elem); if (it == liste.end() || elem != *it) return 0; size_t rank = it - liste.begin(); copy_pair_nt_kmer(liste_value[rank], val); return 1; } AssocPairedSet::AssocPairedSet() { } //return -1 if elem is not in the set int AssocPairedSet::set(set_elem elem, pair_nt_kmer_t val) { vector::iterator it; it = lower_bound(liste.begin(), liste.end(),elem); if (it == liste.end() ||elem != *it) return 0; size_t rank = it - liste.begin(); copy_pair_nt_kmer(val, &liste_value[rank]); return 1; } void AssocPairedSet::start_iterator() { iterator = liste.begin()-1; } bool AssocPairedSet::next_iterator() { iterator++; if (iterator==liste.end()) return false; return true; } void AssocPairedSet::print() { int i; char seq[100]; printf("print %lu elems \n",liste.size()); for (i=0; irewind_all(); kmer_type kmer; while (branches->read_element(&kmer)) liste.push_back(kmer); printf("done loaded branching kmers in AssocPairedSet \n"); } void AssocPairedSet::dump(char * filename) { FILE *file_data; file_data = fopen(filename,"wb"); fwrite(&liste_value[0], sizeof(pair_nt_kmer_t), liste_value.size(), file_data); //1+ printf("liste_value dumped \n"); } void AssocPairedSet::load(char * filename) { FILE *file_data; file_data = fopen(filename,"rb"); printf("loading assoc paired kmer index from file\n"); liste_value.reserve(liste.size()); liste_value.resize(liste.size()); fread(&liste_value[0], sizeof(pair_nt_kmer_t), liste_value.size(), file_data); printf("assoc paired loaded\n"); } void AssocPairedSet::print_total_size() { printf("Assoc paired set size: %li\n",liste.size()); printf("Assoc paired set capacity: listekmer %li liste val%li\n",liste.capacity(),liste_value.capacity()); printf("%li *%li + %li* %li = %li MB \n",liste.capacity(),sizeof(set_elem),liste_value.capacity(),sizeof(set_value_t), (liste.capacity()*sizeof(set_elem)+liste_value.capacity()*sizeof(set_value_t))/1024/1024 ); } kissplice-2.4.0-p1/debruijn-v4/minia/Set.h000644 000765 000024 00000006343 12676004527 020271 0ustar00ishistaff000000 000000 #ifndef Set_h #define Set_h #include #include #include #include "Hash16.h" #include // for log2f #include // for max/min #include // didn't want to "typedef kmer_type set_elem" because Set is an independent file #ifdef _largeint #include "LargeInt.h" typedef LargeInt set_elem; #else #ifdef _ttmath #include "ttmath/ttmath.h" typedef ttmath::UInt set_elem; #else #if (! defined kmer_type) || (! defined _LP64) typedef uint64_t set_elem; #else typedef kmer_type set_elem; #endif #endif #endif using namespace std; // abstract class class Set{ public: virtual void insert(set_elem elem) = 0; virtual void finalize() = 0; virtual bool contains(set_elem elemn) = 0; }; class HashSet : public Set{ Hash16 *hash; public: void insert(set_elem elem); void finalize(); bool contains(set_elem elemn); static int bits_per_element ; HashSet(uint64_t taille_approx); }; class ListSet : public Set{ protected: vector liste; public: void insert(set_elem elem); void finalize(); bool contains(set_elem elemn); //Raluca bool containsNotBinary(set_elem elem); uint64_t capacity() {return (uint64_t)liste.capacity();} static int bits_per_element ; ListSet(uint64_t taille_approx); ListSet(); }; //typedef unsigned char set_value_t; typedef unsigned short int set_value_t; // need 9 bits for Terminator now class AssocSet : public ListSet { vector liste_value; public: int get( set_elem elem, set_value_t * val); int set(set_elem graine, set_value_t val); void finalize(); AssocSet(); void print_total_size(); void clear(); void start_iterator(); bool next_iterator(); vector::iterator iterator; }; //Raluca typedef struct nt_kmer{ char nt; kmer_type prev_kmer; }nt_kmer_t; typedef struct pair_nt_kmer{ nt_kmer_t nk1, nk2; }pair_nt_kmer_t; void copy_nt_kmer (nt_kmer_t from, nt_kmer_t* to); void copy_pair_nt_kmer (pair_nt_kmer_t from, pair_nt_kmer_t* to); class AssocPairedSet : public ListSet { vector liste_value; public: int get(set_elem elem, pair_nt_kmer_t * val); int set(set_elem graine, pair_nt_kmer_t val); void finalize(); AssocPairedSet(); void print_total_size(); void start_iterator(); bool next_iterator(); void load_branching(BinaryBank * branches); void load(char * filename); void dump(char * filename); vector::iterator iterator; void print(); }; class FPSetCascading4 : public Set{ public: Bloom *bloom2, *bloom3, *bloom4; ListSet *false_positives; bool contains(set_elem elemn) { if (bloom2->contains(elemn)) { if (!bloom3->contains(elemn)) return true; else if (bloom4->contains(elemn) && !false_positives->contains(elemn)) return true; } return false; }; void insert(set_elem elem) {fprintf (stderr, "Error can't insert in FPSetCascading!\n"); exit(0); }; void finalize() {fprintf (stderr, "Error can't finalize in FPSetCascading!\n"); exit(0);}; bool is_false_positive(set_elem elemn) {return contains(elemn);}; }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/SortingCount.cpp000644 000765 000024 00000072205 12676004527 022527 0ustar00ishistaff000000 000000 #include "SortingCount.h" #include "inttypes.h" #include // for getrlimit() #if OMP #include "omp.h" #endif #define SINGLE_BAR 1 #define SEP bool clear_cache = false; // clear file cache from memory (for timing only) bool hybrid_mode = false; bool use_hashing = true; // use hashing instead of sorting (better control of memory) float load_factor = 0.7; bool separate_count = false ; // count separately the multiple read sets, only works with use_hashing, needs define SEP and use_hashing=true bool use_compressed_reads = true ; // true; // write compressed read file int optimism = 1; // optimism == 1 mean that we garantee worst case the memory usage, any value above assumes that, on average, a k-mer will be seen 'optimism' times bool output_histo; // main k-mer counting function, shared between minia and dsk // verbose == 0 : stderr progress bar // verbose >= 1 : print basic status // verbose >= 2 : print extra partition information // write_count == True: include kmer count in results file, in that form: // - save kmer count for each kmer in the resulting binary file // - the very first four bytes of the result file are the kmer length void sorting_count(Bank *Sequences, char *prefix, int max_memory, int max_disk_space, bool write_count, int verbose, bool skip_binary_conversion) { // create a temp dir from the prefix char temp_dir[1024]; sprintf(temp_dir,"%s_temp",prefix); // clear the temp folder (needs to be done before estimating disk space) DIR* dp; struct dirent* ep; char p_buf[512] = {0}; dp = opendir(temp_dir); while ( (dp != NULL) && ((ep = readdir(dp)) != NULL)) { sprintf(p_buf, "%s/%s", temp_dir, ep->d_name); remove(p_buf); } if(dp != NULL) closedir(dp); if (max_disk_space == 0) { // default max disk space struct statvfs buffer ; char current_path[1000]; getcwd(current_path,sizeof(current_path)); // int ret = statvfs(current_path, &buffer); uint32_t available = (uint32_t)(((double)buffer.f_bavail * (double)buffer.f_bsize) / 1024.0 / 1024.0); printf("Available disk space in %s: %d MB\n",current_path,available); // not working in osx (is that a TODO then?) uint32_t input_size = max(1, (int)(( (double)(Sequences->filesizes) ) / 1024.0 / 1024.0)); max_disk_space = min(available/2, input_size); } if (max_disk_space == 0) // still 0? max_disk_space = 10000; // = default for osx // estimate number of iterations uint64_t volume = Sequences->estimate_kmers_volume(sizeKmer); uint32_t nb_passes = ( volume / max_disk_space ) + 1; int nb_threads=1; #if OMP use_compressed_reads = true; nb_threads = 8; max_memory /= nb_threads; max_memory = max (max_memory,1); #endif // temp bugfix: don't use compressed reads for long reads if (Sequences->estimate_max_readlen() > 1000000) use_compressed_reads = false; uint64_t volume_per_pass; uint32_t nb_partitions; // loop to lower the number of partitions below the maximum number of simulatenously open files do { volume_per_pass = volume / nb_passes; nb_partitions = ( volume_per_pass / max_memory ) + 1; // if partitions are hashed instead of sorted, adjust for load factor // (as in the worst case, all kmers in the partition are distinct and partition may be slightly bigger due to hash-repartition) if (use_hashing) { nb_partitions = (uint32_t) ceil((float) nb_partitions / load_factor); nb_partitions = ((nb_partitions * OAHash::size_entry()) + sizeof(key_type)-1) / sizeof(key_type); // also adjust for hash overhead nb_partitions = max((int)(nb_partitions/(optimism+1)), 1); if (verbose) printf("Updated number of partitions for hash-based k-mer counting: %d\n",nb_partitions); } // round nb_partitions to mulitple of nthreads, for better perf // nb_partitions = ((nb_partitions + nb_threads - 1) / nb_threads) * nb_threads; if (verbose) printf("Estimate of number of partitions: %d, number of passes: %d\n",nb_partitions, nb_passes); // get max number of open files struct rlimit lim; int max_open_files = 1000; int err = getrlimit(RLIMIT_NOFILE, &lim); if (err == 0) max_open_files = lim.rlim_cur / 2; if (nb_partitions >= max_open_files) { if (verbose) printf("Number of partitions higher than max. number of open files (%d), need to increase the number of passes\n", max_open_files); nb_passes++; } else break; } while (1); // volume / (sizeof(kmer_type)*4) is approx size of read file stored in binary, read nb_passes -1 times uint64_t total_IO = volume * 2LL * 1024LL*1024LL ;// in bytes + nb_passes * ( volume / (sizeof(kmer_type)*4) ) ; // in bytes uint64_t temp_IO = 0; //if (nb_passes==1) use_compressed_reads=false; BinaryBankConcurrent * redundant_partitions_file[nb_partitions]; char redundant_filename[nb_partitions][256]; kmer_type kmer; int max_read_length = KMERSBUFFER_MAX_READLEN; kmer_type * kmer_table_seq = (kmer_type * ) malloc(sizeof(kmer_type)*max_read_length); ; fprintf(stderr,"Sequentially counting ~%llu MB of kmers with %d partition(s) and %d passes using %d thread(s), ~%d MB of memory and ~%d MB of disk space\n", (unsigned long long)volume, nb_partitions,nb_passes, nb_threads, max_memory * nb_threads, max_disk_space); STARTWALL(count); mkdir(temp_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); BinaryBankConcurrent * SolidKmers = new BinaryBankConcurrent(return_file_name(solid_kmers_file),sizeof(kmer),true,nb_threads); if (write_count) { // write k-mer nbits as the first 4 bytes; and actual k-mer size as the next 4 bits uint32_t kmer_nbits = sizeof(kmer) * 8; SolidKmers->write_buffered(&kmer_nbits, 4,0); SolidKmers->write_buffered(&sizeKmer, 4,0); SolidKmers->flush(0); } int64_t estimated_NbReads = Sequences->estimate_nb_reads(); // only used in progress prints char * rseq; int readlen; int64_t NbSolid = 0; int64_t * NbSolid_omp = (int64_t *) calloc(nb_threads,sizeof(int64_t)); #ifdef SEP long total_kmers_per_partition[nb_partitions]; //guillaume probably commented it because updating this variable would require synchronization for (int jj=0; jjclose(); } if(use_compressed_reads && (!skip_binary_conversion)) { char * pt_begin; int idx =0 ; int64_t NbRead = 0; Progress progress_conversion; // progress_conversion.timer_mode=1; // to switch to timer mode (show elapsed and estimated remaining time) progress_conversion.init(estimated_NbReads,"First step: Converting input file into Binary format"); binread = new BinaryReads(return_file_name(binary_read_file),true); #ifdef SEP int file_id =0; int prev_file_id =0; #endif Sequences->rewind_all(); while(1) { #ifdef SEP if(! Sequences->get_next_seq(&rseq,&readlen,&file_id)) break; // read original fasta file if(separate_count && (file_id != prev_file_id)) { //printf("new file \n"); prev_file_id = file_id; binread->mark_newfile(); } #else if(! Sequences->get_next_seq(&rseq,&readlen)) break; // read original fasta file #endif if(readlen > max_read_length) // realloc kmer_table_seq if needed { max_read_length = 2*readlen; kmer_table_seq = (kmer_type * ) realloc(kmer_table_seq,sizeof(kmer_type)*max_read_length); } pt_begin = rseq; //should be ok while (pt_begin < (rseq+ readlen)) { idx=0; // start a new read //skips NN while (*pt_begin =='N' && pt_begin < (rseq+ readlen)) { pt_begin ++; } // goes to next N or end of seq while ( (pt_begin[idx] !='N') && ((pt_begin +idx) < (rseq+ readlen)) ) { idx++; } //we have a seq beginning at pt_begin of size idx ,without any N, will be treated as a read: binread->write_read(pt_begin,idx); pt_begin += idx; } // binread->write_read(rseq,readlen); NbRead++; if ((NbRead%10000)==0) { progress_conversion.inc(10000); } } progress_conversion.finish(); binread->close(); } ///fin conversion if (clear_cache) { #ifdef OSX system("purge"); #else system("echo 3 > /proc/sys/vm/drop_caches"); #endif } #if SINGLE_BAR Progress progress; char message[1000]; sprintf(message,"Counting kmers"); progress.timer_mode=1; if (verbose == 0 ) progress.init(total_IO,message); #endif // nb_passes = how many times we will traverse the whole reads file (has an influence on temp disk space) for (uint32_t current_pass = 0; current_pass < nb_passes; current_pass ++) { #ifdef SEP if(separate_count) { for (int jj=0; jjopen(false); STARTWALL(debpass); STARTWALL(debw); for (uint32_t p=0;prewind_all(); #if !SINGLE_BAR Progress progress; progress.timer_mode=1; // to switch to timer mode (show elapsed and estimated remaining time) char message[1000]; sprintf(message,"Pass %d/%d, Step 1: partitioning",current_pass+1,nb_passes); if (verbose == 0 ) progress.init(estimated_NbReads,message); #endif int file_id=0; //current_pass> 0 && #if OMP #pragma omp parallel if(use_compressed_reads && ! separate_count) num_threads(nb_threads) #endif { int64_t nbkmers_written =0; int tid =0; int64_t NbRead = 0; int64_t nread =0; int64_t tempread =0; #if OMP tid = omp_get_thread_num(); #endif int nreads_in_buffer= 1000; KmersBuffer * kbuff =NULL; if(use_compressed_reads) { kbuff = new KmersBuffer (binread, 1000000, nreads_in_buffer); //buffer size (in nb of kmers), seq per task // the buffer is per thread kbuff->binary_read_file = binread->binary_read_file; } kmer_type * kmer_table ; while(1) { //read the fasta file if(use_compressed_reads) // && current_pass>0 { nread = kbuff->readkmers(); #ifdef SEP if(separate_count && (nread == -2)) { //printf("New file notified, filling parti\n"); for (int kk=0; kk< nb_partitions; kk++) { kmers_perparti_perfile[kk][file_id] = total_kmers_per_partition [kk]; //printf(".. total_kmers_per_partition[%i] = %li\n",kk, total_kmers_per_partition [kk]); } file_id++; continue; } #endif if(nread < 0) break; NbRead+= nread; tempread+= nread; } else { if(! Sequences->get_next_seq(&rseq,&readlen)) break; // read original fasta file if(readlen > max_read_length) // realloc kmer_table_seq if needed { max_read_length = 2*readlen; kmer_table_seq = (kmer_type * ) realloc(kmer_table_seq,sizeof(kmer_type)*max_read_length); } } // if(use_compressed_reads ) //write compressed read file at first pass //&& current_pass==0 // binread->write_read(rseq,readlen); int i; int nbkmers =readlen-sizeKmer+1; if( use_compressed_reads) //current_pass >0 && { nbkmers = kbuff->nkmers; kmer_table = kbuff->kmers_buffer; // printf("nb kmers read %lli \n",nbkmers); // NbRead+= nreads_in_buffer; } else //old fashion { compute_kmer_table_from_one_seq(readlen,rseq,kmer_table_seq); nbkmers =readlen-sizeKmer+1; kmer_table = kmer_table_seq; NbRead++; } //printf("Encountering empty block \n"); nbkmers_written= 0; //compute the kmers stored in the buffer kmer_table for (i=0; i> 14); kmer_hash = (~kmer_hash) + (kmer_hash << 18); kmer_hash = kmer_hash ^ (kmer_hash >> 31); kmer_hash = kmer_hash * 21; kmer_hash = kmer_hash ^ (kmer_hash >> 11); kmer_hash = kmer_hash + (kmer_hash << 6); kmer_hash = kmer_hash ^ (kmer_hash >> 22); // check if this kmer should be included in the current pass if ((kmer_hash % nb_passes ) != current_pass) continue; kmer_type reduced_kmer = kmer_hash / nb_passes; int p;// compute in which partition this kmer falls into #ifdef _ttmath (reduced_kmer % nb_partitions).ToInt(p); #else p = reduced_kmer % nb_partitions; #endif nbkmers_written++; redundant_partitions_file[p]->write_element_buffered(&lkmer,tid); // save this kmer to the right partition file #ifdef SEP if(separate_count) total_kmers_per_partition[p]++; // guillaume probably commented it because updating this variable would require synchronization #endif } //NbRead++; #if SINGLE_BAR if(verbose==0) { if (nb_threads == 1) progress.inc(nbkmers_written * sizeof(kmer_type)); else progress.inc(nbkmers_written * sizeof(kmer_type),tid); } #endif // if ((NbRead%10000)==0) if(tempread> 10000) { tempread -= 10000; if (verbose) fprintf (stderr,"%cPass %d/%d, loop through reads to separate (redundant) kmers into partitions, processed %lluM reads out of %lluM",13,current_pass+1,nb_passes,(unsigned long long)(NbRead/1000/1000),(unsigned long long)(estimated_NbReads/1000/1000)); #if !SINGLE_BAR else if (nb_threads == 1) progress.set(NbRead); else progress.inc(10000,tid); #endif } } //end while if(use_compressed_reads) delete kbuff; } // end OMP #if !SINGLE_BAR if (verbose == 0) { if (nb_threads == 1) progress.finish(); else progress.finish_threaded(); // here only one thread sprintf(message,"Pass %d/%d, Step 2: computing kmer count per partition",current_pass+1,nb_passes); progress.init(nb_partitions+1,message); } #endif if (verbose)fprintf(stderr,"\n"); if (verbose >= 2) { STOPWALL(debw,"Writing redundant kmers"); } STARTWALL(debtri); // close partitions and open them for reading for (uint32_t p=0;pclose(); redundant_partitions_file[p]->open(false); } // for better timing: clear the file cache, since the partitions may still be in memory, that's unfair to low mem machines if (clear_cache) { #ifdef OSX system("purge"); #else system("echo 3 > /proc/sys/vm/drop_caches"); #endif } //quick and dirty parall with omp, testing //todo if we want omp and histo : separate histo_count tab per thread that needs to be merged at the end // TODO to guillaume: remove that todo above, because it is done, right? #if OMP //omp_set_numthreads(2); //num_threads(2) //if(!output_histo) num_threads(nb_threads) #pragma omp parallel for if (! separate_count) private (p) num_threads(nb_threads) #endif // load, sort each partition to output solid kmers for (int p=0;pnb_elements()*sizeof(kmer_type))/1024LL/1024LL); if( (redundant_partitions_file[p]->nb_elements()*sizeof(kmer_type)) < (max_memory*1024LL*1024LL) ) use_hashing_for_this_partition = false; else use_hashing_for_this_partition = true; } int tid =0; #if OMP tid = omp_get_thread_num(); #endif if (use_hashing_for_this_partition) { // hash partition and save to solid file OAHash hash(max_memory*1024LL*1024LL); uint64_t nkmers_read=0; uint64_t nkmers_read_all=0; file_id = 0; while (redundant_partitions_file[p]->read_element_buffered (&lkmer)) { hash.increment(lkmer); nkmers_read++; #ifdef SEP nkmers_read_all++; if( separate_count && (kmers_perparti_perfile[p][file_id]==nkmers_read_all)) { //printf("Parsing parti .. detected end of file %i at %lli parti %i \n",file_id,nkmers_read_all,p); file_id++; //faire raz des cpts < seuil de la hash, devrait etre suffisant ... hash.start_iterator(); while (hash.next_iterator()) { if (hash.iterator->value < nks) { hash.iterator->value = -1 ; // 0 is not valid in oahash, emulate it with -1 } } } #endif #if SINGLE_BAR if(verbose==0 && nkmers_read==10000) { if (nb_threads == 1) progress.inc(nkmers_read*sizeof(kmer_type)); else progress.inc(nkmers_read*sizeof(kmer_type),tid); nkmers_read=0; } #endif } //single bar if (verbose >= 2) printf("Pass %d/%d partition %d/%d hash load factor: %0.3f\n",current_pass+1,nb_passes,p+1,nb_partitions,hash.load_factor()); hash.start_iterator(); while (hash.next_iterator()) { #ifdef SEP int value = hash.iterator->value; if(value==-1) value = 0; // desemulate -1 uint_abundance_t abundance = value; #else uint_abundance_t abundance = hash.iterator->value; #endif if(output_histo) { uint_abundance_t saturated_abundance; saturated_abundance = (abundance >= 10000) ? 10000 : abundance; #if OMP histo_count_omp[tid][saturated_abundance]++; #else //printf("histo_count 0 1 2 %i %i %i \n",histo_count[0],histo_count[1],histo_count[2]); histo_count[saturated_abundance]++; #endif } if (abundance >= nks && abundance <= max_couv) { SolidKmers->write_element_buffered(&(hash.iterator->key),tid); NbSolid_omp[tid]++; if (write_count) SolidKmers->write_buffered(&abundance, sizeof(abundance),tid, false); } distinct_kmers_per_partition[p]++; } } else { // sort partition and save to solid file vector < kmer_type > kmers; uint64_t nkmers_read=0; while (redundant_partitions_file[p]->read_element_buffered (&lkmer)) { kmers.push_back (lkmer); nkmers_read++; #if SINGLE_BAR if(verbose==0 && nkmers_read==10000) { if (nb_threads == 1) progress.inc(nkmers_read*sizeof(kmer_type)); else progress.inc(nkmers_read*sizeof(kmer_type),tid); nkmers_read=0; } #endif } sort (kmers.begin (), kmers.end ()); kmer_type previous_kmer = *(kmers.begin ()); uint_abundance_t abundance = 0; for (vector < kmer_type >::iterator it = kmers.begin (); it != kmers.end (); it++) { kmer_type current_kmer = *it; if (current_kmer == previous_kmer) abundance++; else { if(output_histo) { uint_abundance_t saturated_abundance; saturated_abundance = (abundance >= 10000) ? 10000 : abundance; #if OMP histo_count_omp[tid][saturated_abundance]++; #else histo_count[saturated_abundance]++; #endif } if (abundance >= nks && abundance <= max_couv) { NbSolid_omp[tid]++; SolidKmers->write_element_buffered(&previous_kmer,tid); if (write_count) SolidKmers->write_buffered(&abundance, sizeof(abundance),tid, false); } abundance = 1; distinct_kmers_per_partition[p]++; } previous_kmer = current_kmer; } //last kmer distinct_kmers_per_partition[p]++; if(output_histo) { uint_abundance_t saturated_abundance; saturated_abundance = (abundance >= 10000) ? 10000 : abundance; #if OMP histo_count_omp[tid][saturated_abundance]++; #else histo_count[saturated_abundance]++; #endif } if (abundance >= nks && abundance <= max_couv) { NbSolid_omp[tid]++; SolidKmers->write_element_buffered(&previous_kmer,tid); if (write_count) SolidKmers->write_buffered(&abundance, sizeof(abundance),tid, false); } } if (verbose >= 1) fprintf(stderr,"%cPass %d/%d, loaded and sorted partition %d/%d, found %lld solid kmers so far",13,current_pass+1,nb_passes,p+1,nb_partitions,(long long)(NbSolid_omp[tid])); if (verbose >= 2) printf("\nPass %d/%d partition %d/%d %ld distinct kmers\n",current_pass+1,nb_passes,p+1,nb_partitions,/*total_kmers_per_partition[p],*/distinct_kmers_per_partition[p]); #if !SINGLE_BAR if (verbose == 0 && nb_threads==1) progress.inc(1); else if (verbose == 0 && nb_threads>1) progress.inc(1,tid); #endif redundant_partitions_file[p]->close(); remove(redundant_filename[p]); } // end for partitions #if OMP //merge histo if(output_histo) { for (int cc=1; cc<10001; cc++) { uint64_t sum_omp = 0; for(int ii=0;ii 1 ) progress.finish_threaded(); #endif if (verbose) fprintf(stderr,"\n"); if (verbose >= 2) { STOPWALL(debtri,"Reading and sorting partitions"); STOPWALL(debpass,"Pass total"); } if(use_compressed_reads) binread->close(); //delete for (uint32_t p=0;p 1 ) progress.finish_threaded(); #endif if(output_histo) { FILE * histo_file = fopen(return_file_name(histo_file_name),"w"); for (int cc=1; cc<10001; cc++) { fprintf(histo_file,"%i\t%llu\n",cc,(unsigned long long)(histo_count[cc])); } fclose(histo_file); } free(histo_count); NbSolid = NbSolid_omp[0]; #if OMP NbSolid=0; for(int ii=0;iiclose(); printf("\nSaved %lld solid kmers\n",(long long)NbSolid); rmdir(temp_dir); STOPWALL(count,"Counted kmers"); fprintf(stderr,"\n------------------ Counted kmers and kept those with abundance >=%i, \n",nks); } kissplice-2.4.0-p1/debruijn-v4/minia/SortingCount.h000644 000765 000024 00000001223 12676004527 022164 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include // for S_IRWXU etc #include #include // to determine available disk space #include // to clear the temp directory #ifndef SORTINGCOUNT_H #define SORTINGCOUNT_H #include "Bank.h" #include "Kmer.h" #include "Utils.h" #include "OAHash.h" using namespace std; typedef uint32_t uint_abundance_t; void sorting_count(Bank *Sequences, char *prefix, int max_memory, int max_disk_space, bool write_count, int verbose, bool skip_binary_conversion = false); extern int optimism; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Terminator.cpp000644 000765 000024 00000032416 12676004527 022215 0ustar00ishistaff000000 000000 // // Terminator.cpp // #ifndef ASSERTS #define NDEBUG // disable asserts, they're computationnally intensive #endif #include #include #include #include #include #include // for max #include "Terminator.h" #include "Traversal.h" // for extensions() using namespace::std; bool Terminator::verbose = true; // common terminator functions (actually, more like Kmer64 + bloom + debloom) // we define a structure 2x4 bits which indicates which nucleotides correspond to in- and out-branching of a kmer // let's see an example (bidirected debruijn graph): // // ACT [G] -\/-> [C] CAG [T] <-- [C] CCA // --- X --- --- //[C] AGT -/\- > [A] CTG [G] --> TGG [G] // // structure for the node CAG/CTG: (forward:)0010(reverse:)0001 (the order is ACTG); T forward (to the right) and G reverse (to the right) unsigned char Terminator::branching_structure(kmer_type graine) { assert(graine<=revcomp(graine)); kmer_type new_graine; unsigned char result = 0; int nt; int strand; for(nt=0; nt<4; nt++) { // forward right extensions strand=0; new_graine = next_kmer(graine,nt,&strand); if(bloom_solid_kmers->contains(new_graine) && !debloom->contains(new_graine)){ result|=1<contains(new_graine) && !debloom->contains(new_graine)){ result|=1<<(nt+4); } } return result; } // determines if a kmer is branching or not bool Terminator::is_branching(kmer_type graine) { assert(graine<=revcomp(graine)); // method specific to order=0 (remember that order>0 is never used) if (order == 0) { // cannot really be optimized, because most kmers will be non-branching, hence computing branching_structure() takes optimal time int nb_forward_links = 0, nb_reverse_links = 0; int i; unsigned char branching = branching_structure(graine); for (i=0;i<4;i++) nb_forward_links += (branching>>i)&1; for (i=4;i<8;i++) nb_reverse_links += (branching>>i)&1; return !(nb_forward_links == 1 && nb_reverse_links == 1); } else { if (order==1 && is_tip(graine,bloom_solid_kmers,debloom))// algo fixme-order>0: order=1, tips are ignored as branching kmers. should find a more general code for order>1 return false; // any order>=0: check that this node really yields a true branching, as opposed to branching yielding tips for (int strand=0;strand<2;strand++) { int osef; int nb_extensions = traversal_extensions(graine,strand,osef, bloom_solid_kmers, debloom); if (nb_extensions != 1) return true; } } return false; } // [branching kmers]-based terminator // most kmers should have 1 in-neighbor and 1 out-neighbor // this terminator indexes all the other kmers (ie., branching or dead-end kmers) // it will miss circular regions tho.. // mark a kmer (the kmer has to be a branching kmer or a neighbor of one, else no effect) void BranchingTerminator::mark(kmer_type graine) { assert(graine<=revcomp(graine)); bool could_mark = false; // if it is a branching kmer, mark it directly (it may have no branching neighbor) if (is_indexed(graine)) { set_value_t val; branching_kmers->get(graine,&val); branching_kmers->set(graine,val|(1<<8)); could_mark = true; } // enumerate all the neighbors for (int strand=0; strand < 2; strand++) { for(int nt=0; nt<4; nt++) { // test if the neighbor is a branching kmer int neighbor_strand = strand; kmer_type neighbor = next_kmer(graine,nt,&neighbor_strand); if( (!bloom_solid_kmers->contains(neighbor)) || debloom->contains(neighbor) ) continue; if (!is_indexed(neighbor)) continue; // mark the kmer in that neighbor int revStrand ; int revNT; revStrand = 1-neighbor_strand; if (strand == 0) revNT = revcomp_int(code2nucleotide(graine,0)); else revNT = code2nucleotide(graine,sizeKmer-1); mark(neighbor,revNT,revStrand); could_mark = true; } } if (could_mark) assert(is_marked(graine)); } // record info into a branching kmer void BranchingTerminator::mark(kmer_type graine, char nt, int strand) { assert(nt<4); assert(strand<2); assert(graine<=revcomp(graine)); // BranchingTerminator ignores non-branching kmers if (!is_indexed(graine)) return; //int val = 0; set_value_t val=0; branching_kmers->get(graine,&val); // set a 1 at the right NT & strand position if (strand==0) val|=1<<(nt); else val|=1<<(nt+4); // printf ("mark, graine = %llx val: %x branching structure: %x\n",graine,val,branching_structure(graine)); branching_kmers->set(graine,val); //was insert for Hash16 assert(is_marked(graine,nt,strand)); } // test if a kmer is marked, providing that the kmer is a branching kmer or a neighbor of one (else, considered unmarked) bool BranchingTerminator::is_marked(kmer_type graine) { assert(graine<=revcomp(graine)); // if it is a branching kmer, read marking directly (it may have no branching neighbor) if (is_indexed(graine)) return is_marked_branching(graine); // enumerate all the neighbors for (int strand=0; strand < 2; strand++) { for(int nt=0; nt<4; nt++) { // test if the neighbor is a branching kmer int neighbor_strand = strand; kmer_type neighbor = next_kmer(graine,nt,&neighbor_strand); if( (!bloom_solid_kmers->contains(neighbor)) || debloom->contains(neighbor) ) continue; if ( !is_indexed(neighbor) ) continue; // test the kmer w.r.t that neighbor int revStrand ; int revNT; revStrand = 1-neighbor_strand; if (strand == 0) revNT = revcomp_int(code2nucleotide(graine,0)); else revNT = code2nucleotide(graine,sizeKmer-1); if ( is_marked(neighbor,revNT,revStrand) ) return true; } } return false; } // test if a branching kmer is marked (using the special marker for branching kmers only) bool BranchingTerminator::is_marked_branching(kmer_type graine) { assert(graine<=revcomp(graine)); assert(is_branching(graine)); assert(is_indexed(graine)); set_value_t val; branching_kmers->get(graine,&val); return (val&(1<<8)) != 0; } // that function returns false for non-indexed kmers bool BranchingTerminator::is_marked(kmer_type graine, char nt, int strand) { assert(nt<4); assert(strand<2); assert(graine<=revcomp(graine)); set_value_t val = 0; int is_present = branching_kmers->get(graine,&val); if (!is_present) return false; //printf ("is_marked, graine = %llx val: %x branching structure: %x\n",graine,val,branching_structure(graine)); int extension_nucleotide_marked; if (strand==0) extension_nucleotide_marked = (val>>nt)&1; else extension_nucleotide_marked = (val>>(nt+4))&1; return extension_nucleotide_marked == 1; } bool BranchingTerminator::is_indexed(kmer_type graine) { return branching_kmers->contains(graine); } BranchingTerminator::BranchingTerminator(BinaryBank *given_SolidKmers, uint64_t genome_size, Bloom *given_bloom, Set *given_debloom) : Terminator(given_SolidKmers,given_bloom,given_debloom) { // estimate, from the first million of kmers, the number of branching kmers, extrapolating given the estimated genome size // TODO: erwan noticed that this code isn't useful anymore with AssocSet, feel free to remove it sometimes uint64_t nb_extrapolation = 3000000; SolidKmers->rewind_all(); uint64_t nb_kmers = 0; nb_branching_kmers = 0; kmer_type kmer; uint64_t previous_estimated_nb_branching_kmers, estimated_nb_branching_kmers; while (SolidKmers->read_element(&kmer)) { if (is_branching(kmer)) nb_branching_kmers++; if ((nb_branching_kmers%1000)==0 && nb_branching_kmers>0) { previous_estimated_nb_branching_kmers = estimated_nb_branching_kmers; estimated_nb_branching_kmers = (uint64_t)((1.0*nb_branching_kmers)/nb_kmers * genome_size); // minor todo: stop when previous_.. - estimated < threshold (pourquoi pas = 10% estimated) fprintf (stderr,"%cExtrapolating the number of branching kmers from the first %dM kmers: %lld",13,(int)ceilf(nb_extrapolation/1024.0/1024.0),estimated_nb_branching_kmers); } if (nb_kmers++ == nb_extrapolation) break; } estimated_nb_branching_kmers = (uint64_t)((1.0*nb_branching_kmers)/nb_kmers * genome_size); // final estimation int estimated_NBITS_TERMINATOR = max( (int)ceilf(log2f(estimated_nb_branching_kmers)), 1); fprintf (stderr,"\n"); // call Hash16 constructor // branching_kmers = new Hash16(estimated_NBITS_TERMINATOR); branching_kmers = new AssocSet(); // index, once and for all, all the branching solid kmers SolidKmers->rewind_all(); nb_branching_kmers = 0; uint64_t nb_solid_kmers = 0; while (SolidKmers->read_element(&kmer)) { if (is_branching(kmer)) { // branching_kmers->insert(kmer,0); branching_kmers->insert(kmer); nb_branching_kmers++; } nb_solid_kmers++; if ((nb_branching_kmers%500)==0) fprintf (stderr,"%cIndexing branching kmers %lld / ~%lld",13,nb_branching_kmers,estimated_nb_branching_kmers); } if (nb_branching_kmers == 0) printf("\n**** Warning\n\nNo branching kmers were found in this dataset (it is either empty or a tiny circular genome) - Minia will not assemble anything.\n\n****\n\n"); branching_kmers->finalize(); // branching_kmers->print_total_size(); // fprintf (stderr,"\n\nAllocated memory for marking: %lld branching kmers x (%lu+%lu) B \n",nb_branching_kmers,sizeof(kmer_type),sizeof(set_value_t)); // fprintf (stderr," actual implementation: (sizeof(kmer_type) = %lu B) + (sizeof(set_value_t) = %lu B) per entry: %.2f bits / solid kmer\n",sizeof(kmer_type),sizeof(set_value_t),(nb_branching_kmers*((sizeof(kmer_type)+sizeof(set_value_t))*8.0))/nb_solid_kmers); // init branching_kmers iterator for what happens next branching_kmers->start_iterator(); } // constructor that simply loads a dump of branching kmers BranchingTerminator::BranchingTerminator(BinaryBank *branchingKmers, BinaryBank *given_SolidKmers, Bloom *given_bloom, Set *given_debloom) : Terminator(given_SolidKmers,given_bloom,given_debloom) { nb_branching_kmers = branchingKmers->nb_elements(); int NBITS_TERMINATOR = max( (int)ceilf(log2f(nb_branching_kmers)), 1); // call Hash16 constructor // branching_kmers = new Hash16(NBITS_TERMINATOR); branching_kmers = new AssocSet(); // load branching kmers branchingKmers->rewind_all(); kmer_type kmer; while (branchingKmers->read_element(&kmer)) branching_kmers->insert(kmer); //,0 for Hash16 branching_kmers->finalize(); if (verbose) fprintf (stderr,"\nLoaded %lld branching kmers x %lu B = %.1f MB\n",nb_branching_kmers,sizeof(cell),((1<)*1.0)/1024.0/1024.0); // init branching_kmers iterator for what happens next branching_kmers->start_iterator(); } BranchingTerminator::~BranchingTerminator() { delete branching_kmers; } bool BranchingTerminator::next(kmer_type *kmer) { if (branching_kmers->next_iterator()) { //*kmer = branching_kmers->iterator.cell_ptr->graine; //for Hash16 *kmer = *(branching_kmers->iterator); return true; } return false; } void BranchingTerminator::dump_branching_kmers(BinaryBank *BranchingKmers) { // init branching_kmers iterator for what happens next branching_kmers->start_iterator(); while (branching_kmers->next_iterator()) { //kmer_type kmer = branching_kmers->iterator.cell_ptr->graine;//for Hash16 kmer_type kmer = *( branching_kmers->iterator); BranchingKmers->write_element(&kmer); } fprintf (stderr,"Dumped branching kmers\n"); } // reset all marking information: everything is now unmarked void BranchingTerminator::reset() { branching_kmers->clear(); } //-------------------------------------------------------- // bloom-based terminator (untested, totally unused) BloomTerminator::BloomTerminator(int tai_Bloom) { bloo2 = new Bloom(tai_Bloom); // to mark kmers already used for assembly ... taille a changer } void BloomTerminator::mark(kmer_type graine, char nt, int strand) { bloo2->add(graine); } bool BloomTerminator::is_marked(kmer_type graine, char nt, int strand) { return bloo2->contains(graine); } bool BloomTerminator::is_fully_marked(kmer_type graine) { return is_marked(graine,0,0); } bool BloomTerminator::next(kmer_type *kmer) { return SolidKmers->read_element(kmer); } kissplice-2.4.0-p1/debruijn-v4/minia/Terminator.h000644 000765 000024 00000004402 12676004527 021654 0ustar00ishistaff000000 000000 // // Terminator.h #ifndef Terminator_h #define Terminator_h #include #include #include #include // for log2f #include "Bloom.h" #include "Kmer.h" #include "Set.h" #include "Bank.h" class Terminator{ protected: BinaryBank *SolidKmers; Bloom *bloom_solid_kmers; Set *debloom; unsigned char branching_structure(kmer_type graine); public: static bool verbose; virtual void mark(kmer_type graine, char nt, int strand){ return; }; virtual bool is_marked(kmer_type graine, char nt, int strand){ return (1); }; virtual void mark(kmer_type graine){ return; }; virtual bool is_marked(kmer_type graine){ return (1); }; virtual bool is_marked_branching(kmer_type graine) {return 1;}; virtual void reset() {return;}; bool is_branching(kmer_type graine); bool next(kmer_type *kmer); Terminator(BinaryBank *given_SolidKmers, Bloom *given_bloom, Set *given_debloom) : SolidKmers(given_SolidKmers), bloom_solid_kmers(given_bloom), debloom(given_debloom) { } }; class BloomTerminator { protected: Bloom * bloo2; BinaryBank *SolidKmers; public: // is there a way to not repeat the declaration of Terminator functions? bool is_fully_marked(kmer_type graine); void mark(kmer_type graine, char nt, int strand); bool is_marked(kmer_type graine, char nt, int strand); bool next(kmer_type *kmer); BloomTerminator(int tai_Bloom); ~BloomTerminator(); }; class BranchingTerminator: public Terminator{ bool is_indexed(kmer_type graine); int genome_size; // Hash16 *branching_kmers; AssocSet *branching_kmers; public: bool is_fully_marked(kmer_type graine); void mark(kmer_type graine, char nt, int strand); bool is_marked(kmer_type graine, char nt, int strand); void mark(kmer_type graine); bool is_marked(kmer_type graine); bool is_marked_branching(kmer_type graine); bool next(kmer_type *kmer); int64_t nb_branching_kmers; void dump_branching_kmers(BinaryBank *BranchingKmers); void reset(); BranchingTerminator(BinaryBank *given_SolidKmers, uint64_t genome_size, Bloom *given_bloom, Set *given_debloom); BranchingTerminator(BinaryBank *branchingKmers, BinaryBank *given_SolidKmers, Bloom *given_bloom, Set *given_debloom); ~BranchingTerminator(); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Traversal.cpp000644 000765 000024 00000102201 12676004527 022022 0ustar00ishistaff000000 000000 #include #include // for max() #include "Traversal.h" using namespace std; Traversal::~Traversal() { } void Traversal::set_maxlen(int given_maxlen) { maxlen = given_maxlen; } void Traversal::commit_stats() { final_stats = stats; } void Traversal::revert_stats() { stats = final_stats; } // -------------------------- // generic structure for traversals void Traversal::set_max_depth(int given_max_depth) { max_depth = given_max_depth; } void Traversal::set_max_breadth(int given_max_breadth) { max_breadth = given_max_breadth; } // mark recorded extensions void Traversal::mark_extensions(set *extensions_to_mark) { if (terminator == NULL) return; for(set::iterator it = extensions_to_mark->begin(); it != extensions_to_mark->end() ; ++it) { terminator->mark(*it); } } // return the number of extension possibilities, the variable nt will contain one of them // order=0: just examine immediate neighbors // order>0: don't return extensions yielding to deadends of length <= order // todo-order>0: there is probably a minor bug: it is likely to return 0 extensions too early for end of contigs int traversal_extensions(kmer_type kmer, int strand, int &nt, Bloom *bloom_solid_kmers, Set *debloom) { if (order==0) // faster order=0 extensions (note: in fact, order is always equal to 0) { int nb_extensions = 0; for(int test_nt=0; test_nt<4; test_nt++) { int current_strand = strand; kmer_type current_kmer = next_kmer(kmer,test_nt,¤t_strand); if (bloom_solid_kmers->contains(current_kmer) && !debloom->contains(current_kmer)) { nt = test_nt; nb_extensions ++; } } return nb_extensions; } else { if (order==1) // optimized code for order=1 (copied from assemb.cpp) { int nb_extensions = 0; for(int test_nt=0; test_nt<4; test_nt++) { int current_strand = strand; kmer_type current_kmer = next_kmer(kmer,test_nt, ¤t_strand); if(bloom_solid_kmers->contains(current_kmer) && !debloom->contains(current_kmer)){ bool is_linked = false; for(int tip_nt=0; tip_nt<4; tip_nt++) { int new_strand = current_strand; kmer_type kmer_after_possible_tip = next_kmer(current_kmer,tip_nt, &new_strand); if(bloom_solid_kmers->contains(kmer_after_possible_tip) && !debloom->contains(kmer_after_possible_tip)) { is_linked = true; break; } } if (!is_linked) continue; // it's a tip, because it's linked to nothing nt = test_nt; nb_extensions++; } } return nb_extensions; } else { // slower, general code for order>=0 Frontline frontline( kmer, strand, bloom_solid_kmers, debloom, NULL, 0); while (frontline.depth <= order) // go one step further than order { frontline.go_next_depth(); if (frontline.size() <= 1) // stop when no more ambiguous choices break; if (frontline.size() > 10) // don't allow a breadth too large anyway break; } if (frontline.size() > 0) // recover the nt that lead to this node nt = frontline.front().nt; return frontline.size(); } } } int Traversal::extensions(kmer_type kmer, int strand, int &nt) { return traversal_extensions(kmer,strand,nt,bloom,debloom); } /* * this function is actually only used when order > 0 * tip is: * one strand: 0 extension * other strand: 1 extension to N * N: >= 3 neighbors * */ bool is_tip(kmer_type kmer, Bloom *bloom_solid_kmers, Set *debloom) { int nb_extensions[2]={0}; kmer_type N=0; int N_strand=0; for (int strand=0;strand<2; strand++) { for(int test_nt=0; test_nt<4; test_nt++) { int current_strand = strand; kmer_type current_kmer = next_kmer(kmer,test_nt, ¤t_strand); if(bloom_solid_kmers->contains(current_kmer) && !debloom->contains(current_kmer)){ N = current_kmer; N_strand = current_strand; nb_extensions[strand]++; } } } /* if (nb_extensions[0] != 0 && nb_extensions[1] != 0) return false;*/ // fixme-order>0: too strict, because includes ends of contigs if (nb_extensions[0] == 0 || nb_extensions[1] == 0) return true; // now test degree of N int N_degree = 0; for(int test_nt=0; test_nt<4; test_nt++) { int current_strand = N_strand; kmer_type current_kmer = next_kmer(N,test_nt, ¤t_strand); if(bloom_solid_kmers->contains(current_kmer) && !debloom->contains(current_kmer)){ N_degree++; } } return N_degree>=3; } // from a branching kmer, get a new node that has never been used before // (very simple initial k-mer selection, current used in minia-graph) bool Traversal::get_new_starting_node(kmer_type branching_kmer, kmer_type &starting_kmer) { char newNT[2]; int nt; // start with the branching kmer itself if ( ! terminator->is_marked(branching_kmer) ) { terminator->mark(branching_kmer); starting_kmer = branching_kmer; return true; } for (int current_strand = 0; current_strand<2 ; current_strand++) { for(nt=0; nt<4; nt++) { int strand = current_strand; kmer_type current_kmer; current_kmer = next_kmer(branching_kmer,nt,&strand); if (bloom->contains(current_kmer) && !debloom->contains(current_kmer)) { // only start from an unmarked nt/strand combo if (terminator->is_marked(current_kmer)) continue; terminator->mark(current_kmer); starting_kmer = current_kmer; return true; } } } return false; } // improved version of the code above // TODO: port minia-graph to use this version, make sure it doesn't break the simple paths traversal bool Traversal::get_new_starting_node_improved(kmer_type branching_kmer, kmer_type &starting_kmer) { char newNT[2]; int nt; for (int current_strand = 0; current_strand<2 ; current_strand++) { for(nt=0; nt<4; nt++) { int strand = current_strand; kmer_type current_kmer; current_kmer = next_kmer(branching_kmer,nt,&strand); if (bloom->contains(current_kmer) && !debloom->contains(current_kmer)) { // alright let's use this convention now: // to select new kmers: mark non-branching neighbors of branching nodes // to mark as used in assembly: mark branching nodes // only start from a non-branching k-mer if (terminator->is_branching(current_kmer)) continue; if (terminator->is_marked(current_kmer)) continue; terminator->mark(current_kmer); starting_kmer = current_kmer; return true; } } } // actually never start in a branching k-mer // if ( ! terminator->is_marked(branching_kmer) ) // { // terminator->mark(branching_kmer); // starting_kmer = branching_kmer; // return true; // } return false; } /* the initial k-mer selection function from original Minia release (up until fall 2013) was: detect a 2k+2 simple path (anything NOT deadend or snp) around the branching kmer and start to extend from it the rationale was: -> what if it's a simplepathtraversal and it chooses to start in a deadend? -> what if it's a monumenttraversal and the kmer is a true branching: won't be traversed yet, branching kmers are the only indexed ones //bool Traversal::find_starting_kmer_inside_simple_path(kmer_type branching_kmer, kmer_type &starting_kmer) * this function was unused since fall 2013 and to prevent bugs due to new marking conventions, it has been removed on 10 Feb 2014. * now we use this: -> get a better starting point than a branching kmer inside a long (~2k) simple path: -> a k-mer that isn't inside a bubble/tip */ bool MonumentTraversal::find_starting_kmer(kmer_type branching_kmer, kmer_type &starting_kmer) { char newNT[2]; int nt; bool debug=false; int sum_depths = 0; if (!get_new_starting_node_improved(branching_kmer, starting_kmer)) return false; if (debug) printf("getting new starting kmer\n"); for (int strand = 0; strand<2 ; strand++) { kmer_type previous_kmer = 0; int previous_strand = 0; // do a BFS to make sure we're not inside a bubble or tip Frontline frontline( starting_kmer, strand, bloom, debloom, terminator, NULL, 0, false); do { bool should_continue = frontline.go_next_depth(); if (!should_continue) { if (debug) printf("strand %d shouldnt continue\n", strand); break; } // put the same contraints as in a bubble if (frontline.depth > max_depth || frontline.size() > max_breadth) { if (debug) printf("strand %d reached max depth or breadth (%d %d)\n",strand, frontline.depth,frontline.size()); break; } // stopping condition: nothing more to explore if (frontline.size() == 0) { if (debug) printf("strand %d nothing more to explore\n", strand); break; } char useless_string[max_depth+1]; int useless_int; if (frontline.size() <= 1) { kmer_type current_kmer = 0; if (frontline.size() == 1) { node current_node = frontline.front(); current_kmer = current_node.kmer; } if ((previous_kmer != 0) && terminator->is_branching(previous_kmer)) { /* the current situation is: * * current_kmer previous_kmer * -O-------------O------------- ... ---starting_kmer * \_.... * * or * * [no extension] previous_kmer * X O------------- ... ---starting_kmer * \_.... * * so, by looking one k-mer ahead, we make sure that previous_kmer only branches to the right * */ set all_involved_extensions; Terminator *save_terminator = terminator; terminator = NULL; // do not use terminator in the following bubble traversal if (explore_branching(previous_kmer, 1-previous_strand, (char*)useless_string, useless_int, current_kmer, &all_involved_extensions)) { if (debug) printf("depth %d useless int %d and starting belongs %d nb involved nodes %lu\n",frontline.depth,useless_int,all_involved_extensions.find(starting_kmer) != all_involved_extensions.end(),all_involved_extensions.size()); if (all_involved_extensions.find(starting_kmer) != all_involved_extensions.end()) { terminator = save_terminator; return false; // starting_kmer is in a tip/bubble starting from current_kmer } } terminator = save_terminator; } } // update previous_kmer if (frontline.size() == 1) { node current_node = frontline.front(); kmer_type current_kmer = current_node.kmer; previous_kmer = current_kmer; previous_strand = current_node.strand; } else previous_kmer = 0; } while (1); if (debug) printf("strand %d depth %d\n",strand,frontline.depth); sum_depths += frontline.depth; } // don't even assemble those regions which have no chance to yield a long contig if (sum_depths < (sizeKmer+1)) return false; return true; } // main traversal function, which calls avance() // important: // for MonumentTraversal, either "previous_kmer" is unspecified and then "starting_kmer" is required to be non-branching, // or, if starting_kmer is branching, please specify the "previous_kmer" parameter, corresponding to a left k-mer that will // be ignored during in-branching checks int Traversal::traverse(kmer_type starting_kmer, char* resulting_sequence, int starting_strand, kmer_type previous_kmer) { kmer_type current_kmer = starting_kmer; int current_strand = starting_strand; // 0 = forward, 1 = reverse; int len_extension = 0; char newNT[max_depth+1]; int nnt = 0; bool looping = false; int bubble_start, bubble_end; bubbles_positions.clear(); //printf(" traversing %llX strand:%d\n",starting_kmer,current_strand); while( (nnt=avance(current_kmer, current_strand, len_extension == 0, newNT, previous_kmer))) { if (nnt < 0) // found branching or marked kmers break; if (nnt > 1) // it's a bubble for sure bubble_start = len_extension; // keep re-walking the nucleotides we just discovered, to append to consensus and mark kmers as we go for (int cur_nt = 0; cur_nt < nnt; cur_nt++) { resulting_sequence[len_extension]=newNT[cur_nt]; len_extension++; previous_kmer = current_kmer; current_kmer = next_kmer(current_kmer,NT2int(newNT[cur_nt]),¤t_strand); #ifndef DONTMARK terminator->mark(current_kmer); // mark kmer as used in the assembly #endif if (current_kmer == starting_kmer) // perfectly circular regions with no large branching can happen (rarely) looping = true; } if (nnt > 1) { bubble_end = len_extension; bubbles_positions.push_back(std::make_pair(bubble_start,bubble_end)); } if (looping) break; if (len_extension > maxlen) { // fprintf(stderr,"max contig len reached \n"); break; } } resulting_sequence[len_extension]='\0'; return len_extension; } // ---------------- // random branching traversal char Traversal::random_unmarked_avance(kmer_type kmer, int current_strand, bool first_extension, char * newNT) { char bin2NT[4] = {'A','C','T','G'}; int nt; for(nt=0; nt<4; nt++) //takes first branch we find { int strand = current_strand; kmer_type new_graine = next_kmer(kmer,nt,&strand); if(bloom->contains(new_graine) && !debloom->contains(new_graine) && !terminator->is_marked(kmer, nt, current_strand)){ *newNT = bin2NT[nt]; return 1; } } return 0; } char RandomBranchingTraversal::avance(kmer_type kmer, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer) { return random_unmarked_avance(kmer,current_strand,first_extension,newNT); } // ---------------- // simple paths traversal // invariant: the input kmer has no in-branching. // returns: // 1 if a good extension is found // 0 if a deadend was reached // -1 if out-branching was detected // -2 if no out-branching but next kmer has in-branching int Traversal::simple_paths_avance(kmer_type kmer, int strand, bool first_extension, char * newNT) { char bin2NT[4] = {'A','C','T','G'}; int nb_extensions = 0, in_branching_degree = 0; int good_nt; // return the number of possible forward extensions nb_extensions = extensions(kmer, strand, good_nt); if (nb_extensions == 1) { // if the next kmer has in-branching, don't extend the current kmer int second_strand = strand; kmer_type second_kmer = next_kmer(kmer,good_nt,&second_strand); int osef; in_branching_degree = extensions(second_kmer,1-second_strand,osef); if (in_branching_degree > 1) return -2; *newNT = bin2NT[good_nt]; return 1; } if (nb_extensions > 1) // if this kmer has out-branching, don't extend it return -1; return 0; } char SimplePathsTraversal::avance(kmer_type kmer, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer) { return max(simple_paths_avance(kmer,current_strand,first_extension,newNT),0); } // ---------------- // // Monument traversal // a frontline is a set of nodes having equal depth in the BFS Frontline::Frontline(kmer_type starting_kmer, int starting_strand, Bloom *bloom, Set *debloom, Terminator *terminator, set *all_involved_extensions, kmer_type previous_kmer, bool check_in_branching) : starting_kmer(starting_kmer), starting_strand(starting_strand), bloom(bloom), debloom(debloom), terminator(terminator), all_involved_extensions(all_involved_extensions), previous_kmer(previous_kmer), check_in_branching(check_in_branching), depth(0) { already_frontlined.insert(starting_kmer); already_frontlined.insert(previous_kmer); node first_node(starting_kmer,starting_strand,-1); frontline.push(first_node); } bool Frontline::go_next_depth() { // extend all nodes in this frontline simultaneously, creating a new frontline stopped_reason=NONE; queue_nodes new_frontline; while (!frontline.empty()) { node current_node = frontline.front(); frontline.pop(); kmer_type current_kmer = current_node.kmer; int current_strand = current_node.strand; // make sure this node doesn't have large in-branching. if ( check_in_branching && check_inbranching(current_kmer,current_strand)) { //printf("######## found large in-branching (depth=%d)\n",depth); return false; // detected that the bubble isn't simple (there is large in-branching inside) } // enqueue all neighbors of this node, except these that were already in a frontline for(int nt=0; nt<4; nt++) { kmer_type new_kmer = current_kmer; int new_strand = current_strand; // propagate information where this node comes from int from_nt = (current_node.nt == -1) ? nt : current_node.nt; // go to next kmer new_kmer = next_kmer(new_kmer,nt,&new_strand); // test if that node hasn't already been explored if (already_frontlined.find(new_kmer)!= already_frontlined.end()) continue; if(bloom->contains(new_kmer) && ((debloom == NULL) || (!debloom->contains(new_kmer)))) { // if this bubble contains a marked (branching) kmer, stop everyone at once (to avoid redundancy) if (terminator != NULL && terminator->is_branching(new_kmer)) if (terminator->is_marked_branching(new_kmer)) { stopped_reason=Frontline::MARKED; return false; } node new_node(new_kmer,new_strand,from_nt); new_frontline.push(new_node); already_frontlined.insert(new_kmer); //if (check_in_branching) //printf("frontline _depth: %d enqueuing kmer %s\n",depth ,print_kmer(new_kmer)); // since this extension is validated, insert into the list of involved ones if (all_involved_extensions != NULL) all_involved_extensions->insert(new_kmer); } } } frontline = new_frontline; ++depth; return true; } int Frontline::size() { return frontline.size(); } node Frontline::front() { return frontline.front(); } // new code, not in monument, to detect any in-branching longer than 3k bool Frontline::check_inbranching(kmer_type from_kmer, int from_strand) { int nt; for(nt=0; nt<4; nt++) { int strand = 1-from_strand; kmer_type current_kmer; current_kmer = next_kmer(from_kmer,nt,&strand); // only check in-branching from kmers not already frontlined // which, for the first extension, includes the previously traversed kmer (previous_kmer) // btw due to avance() invariant, previous_kmer is always within a simple path if (already_frontlined.find(current_kmer) != already_frontlined.end()) continue; if (bloom->contains(current_kmer) && !debloom->contains(current_kmer)) { // create a new frontline inside this frontline to check for large in-branching (i know, we need to go deeper, etc..) Frontline frontline( current_kmer, strand, bloom, debloom, terminator, all_involved_extensions,from_kmer, false); do { bool should_continue = frontline.go_next_depth(); if (!should_continue) { stopped_reason=Frontline::IN_BRANCHING_OTHER; break; } // don't allow a depth > 3k if (frontline.depth > 3 * sizeKmer) { stopped_reason=Frontline::IN_BRANCHING_DEPTH; break; } // don't allow a breadth too large if (frontline.size()> 10) { stopped_reason=Frontline::IN_BRANCHING_BREADTH; break; } // stopping condition: no more in-branching if (frontline.size() == 0) break; } while (1); if (frontline.size() > 0) return true; // found large in-branching } } // didn't find any in-branching return false; } // similar to Monument's extension_graph.py:find_end_of_branching // basically do a bounded-depth, bounded-breadth BFS int MonumentTraversal::find_end_of_branching(kmer_type starting_kmer, int starting_strand, kmer_type &end_kmer, int &end_strand, kmer_type previous_kmer, set *all_involved_extensions) { bool check_in_branching = true; Frontline frontline( starting_kmer, starting_strand, bloom, debloom, terminator, all_involved_extensions, previous_kmer, check_in_branching); do { bool should_continue = frontline.go_next_depth(); if (!should_continue) { if (frontline.stopped_reason == Frontline::MARKED) stats.couldnt_because_marked_kmer++; if (frontline.stopped_reason == Frontline::IN_BRANCHING_DEPTH) stats.couldnt_inbranching_depth++; if (frontline.stopped_reason == Frontline::IN_BRANCHING_BREADTH) stats.couldnt_inbranching_breadth++; if (frontline.stopped_reason == Frontline::IN_BRANCHING_OTHER) stats.couldnt_inbranching_other++; return 0; } // don't allow a depth too large if (frontline.depth > max_depth) { stats.couldnt_traverse_bubble_depth++; return 0; } // don't allow a breadth too large if (frontline.size()> max_breadth) { stats.couldnt_traverse_bubble_breadth++; return 0; } // stopping condition: frontline is either empty, or contains only 1 kmer // needs the kmer to be non-branching, in order to avoid a special case of bubble immediatly after a bubble // affects mismatch rate in ecoli greatly if (frontline.size() == 0) { stats.couldnt_find_extension++; return 0; } // if (frontline.size() == 1) // longer contigs but for some reason, higher mismatch rate if (frontline.size() == 1 && (terminator == NULL || ( !terminator->is_branching(frontline.front().kmer) ))) break; } while (1); if (frontline.size()==1) { node end_node = frontline.front(); end_kmer = end_node.kmer; end_strand = end_node.strand; return frontline.depth; } return 0; } // similar to Monument's extension_graph.py:all_paths_between set MonumentTraversal::all_consensuses_between(kmer_type start_kmer, int start_strand, kmer_type end_kmer, int end_strand, int traversal_depth, set used_kmers, string current_consensus, bool &success) { char bin2NT[4] = {'A','C','T','G'}; //printf("all consensuses between traversal_depth: %d kmer %s success %d\n",traversal_depth,print_kmer(start_kmer),success); set consensuses; // find_end_of_branching and all_consensues_between do not always agree on clean bubbles ends // until I can fix the problem, here is a fix // to reproduce the problem: SRR001665.fasta 21 4 if (traversal_depth < -1) { success = false; return consensuses; } if (start_kmer == end_kmer)// not testing for end_strand anymore because find_end_of_branching doesn't care about strands { consensuses.insert(current_consensus); return consensuses; } // visit all neighbors for(int nt=0; nt<4; nt++) { // craft neighbor node int new_strand = start_strand; kmer_type new_graine = next_kmer(start_kmer,nt,&new_strand); // check if the neighbor node is valid if(bloom->contains(new_graine) && !debloom->contains(new_graine)){ // don't resolve bubbles containing loops // (tandem repeats make things more complicated) // that's a job for a gapfiller if (used_kmers.find(new_graine) != used_kmers.end()) { success = false; return consensuses; } // generate extended consensus sequence string extended_consensus(current_consensus); extended_consensus.append(1,bin2NT[nt]); // generate list of used kmers (to prevent loops) set extended_kmers(used_kmers); extended_kmers.insert(new_graine); // recursive call to all_consensuses_between set new_consensuses = all_consensuses_between(new_graine, new_strand, end_kmer, end_strand, traversal_depth - 1, extended_kmers, extended_consensus, success); consensuses.insert(new_consensuses.begin(), new_consensuses.end()); // mark to stop we end up with too many consensuses if (consensuses.size() > (unsigned int )max_breadth) success = false; } // propagate the stop if too many consensuses reached if (success == false) return consensuses; } return consensuses; } // just a wrapper set MonumentTraversal::all_consensuses_between(kmer_type start_kmer, int start_strand, kmer_type end_kmer, int end_strand, int traversal_depth, bool &success) { set used_kmers; used_kmers.insert(start_kmer); string current_consensus; success = true; //printf("all cons between - end kmer = %s\n",print_kmer(end_kmer)); return all_consensuses_between(start_kmer, start_strand, end_kmer, end_strand, traversal_depth, used_kmers, current_consensus, success); } // similar to Monument's extension_graph.py:validate_paths // return true if, basically, the consensuses aren't too different bool MonumentTraversal::validate_consensuses(set consensuses, char *result, int &result_length) { bool debug = false; // compute mean and stdev of consensuses int mean = 0; int path_number = 0; for(set::iterator it = consensuses.begin(); it != consensuses.end() ; ++it) { if (debug) printf("bubble path %d: %s (len=%lu)\n",path_number,(*it).c_str(),(*it).length()); mean+=(*it).length(); path_number++; } mean/=consensuses.size(); double stdev = 0; for(set::iterator it = consensuses.begin(); it != consensuses.end() ; ++it) { int consensus_length = (*it).length(); stdev += pow(fabs(consensus_length-mean),2); } stdev = sqrt(stdev/consensuses.size()); // don't traverse large bubbles if (mean > max_depth) return false; // don't traverse large deadends (here, having one consensus means the other paths were large deadends) if (consensuses.size() == 1 && mean > sizeKmer+1) // deadend length should be < k+1 (most have length 1, but have seen up to 10 in ecoli) return false; if (debug) printf("%lu-bubble mean %d, stdev %.1f\n",consensuses.size(),mean,stdev); // traverse bubbles if paths have roughly the same length if (stdev>mean/5) return false; // check that all consensuses are similar if (! all_consensuses_almost_identical(consensuses)) return false; // if all good, an arbitrary consensus is chosen string chosen_consensus = *consensuses.begin(); result_length = chosen_consensus.length(); if (result_length> max_depth) // it can happen that consensus is longer than max_depth, despite that we didn't explore that far (in a messy bubble with branchings inside) return false; chosen_consensus.copy(result, result_length); return true; } bool MonumentTraversal::all_consensuses_almost_identical(set consensuses) { for (set::iterator it_a = consensuses.begin(); it_a != consensuses.end(); it_a++) { set::iterator it_b = it_a; advance(it_b,1); while (it_b != consensuses.end()) { if (needleman_wunch(*it_a,*it_b) * 100 < consensuses_identity) return false; advance(it_b,1); } } return true; } // similar to Monument's extension_graph.py:explore_branching // return true if the branching can be traversed, and mark all involved nodes bool MonumentTraversal::explore_branching(kmer_type start_kmer, int start_strand, char *consensus, int &consensus_length, kmer_type previous_kmer, set *all_involved_extensions) { kmer_type end_kmer; int end_strand; // find end of branching, record all involved extensions (for future marking) // it returns false iff it's a complex bubble int traversal_depth = find_end_of_branching(start_kmer, start_strand, end_kmer, end_strand, previous_kmer, all_involved_extensions); if (!traversal_depth) { stats.couldnt_find_all_consensuses++; return false; } // find all consensuses between start node and end node set consensuses; bool success; consensuses = all_consensuses_between(start_kmer, start_strand, end_kmer, end_strand, traversal_depth+1, success); // if consensus phase failed, stop if (!success) return false; // validate paths, based on identity bool validated = validate_consensuses(consensuses, consensus, consensus_length); if (!validated) { stats.couldnt_validate_consensuses++; return false; } // the consensuses agree, mark all the involved extensions // (corresponding to alternative paths we will never traverse again) mark_extensions(all_involved_extensions); return true; } // wrapper bool MonumentTraversal::explore_branching(kmer_type start_kmer, int start_strand, char *consensus, int &consensus_length, kmer_type previous_kmer) { set *all_involved_extensions = new set; bool res = explore_branching(start_kmer, start_strand, consensus, consensus_length, previous_kmer, all_involved_extensions); delete all_involved_extensions; return res; } // invariant here: // kmer is always obtained after traversing a non-branching kmer // in other words, the only in-branching of that kmer is previous_kmer char MonumentTraversal::avance(kmer_type kmer, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer) { // if we're on a simple path, just traverse it int is_simple_path = simple_paths_avance(kmer, current_strand, first_extension, newNT); if (is_simple_path > 0) return 1; // the following function does: // * a bfs from the starting kmer, stopping when: // - breadth > max_breadth // or // - depth > max_depth // * check if there a single end point // * computing all possible paths between start and end // * returns one flattened consensus sequence int newNT_length; bool success = explore_branching(kmer, current_strand, newNT, newNT_length, previous_kmer); if (!success) { stats.ended_traversals++; return 0; } return newNT_length; } kissplice-2.4.0-p1/debruijn-v4/minia/Traversal.h000644 000765 000024 00000015353 12676004527 021502 0ustar00ishistaff000000 000000 #ifndef Traversal_h #define Traversal_h #include #include #include #include // only for pow() #include "Kmer.h" #include "Terminator.h" #include "Bloom.h" #include "Set.h" #include "Utils.h" // for needleman_wunch using namespace std; // types using in advanced traversal functions struct kmer_strand_nt { kmer_type kmer; int strand; int nt; kmer_strand_nt(kmer_type kmer, int strand, int nt) : kmer(kmer), strand(strand), nt(nt) {} bool operator<(const kmer_strand_nt &other) const { // need to define a strict weak ordering if (kmer != other.kmer) return (kmer < other.kmer); if (strand != other.strand) return (strand < other.strand); return (nt < other.nt); } }; // in traversals, a node is just a tuple (kmer, strand, nt initially chosen to reach that node) // so it just happens that it's the same type as kmer_strand_nt typedef kmer_strand_nt node; typedef queue queue_nodes; // some stats struct TraversalStats { long ended_traversals; long couldnt_find_all_consensuses; long couldnt_validate_consensuses; long couldnt_traverse_bubble_breadth; long couldnt_traverse_bubble_depth; long couldnt_because_marked_kmer; long couldnt_inbranching_depth; long couldnt_inbranching_breadth; long couldnt_inbranching_other; long couldnt_find_extension; }; // semi-abstract class. implements traverse but not avance class Traversal{ protected: Bloom *bloom; Set *debloom; Terminator *terminator; int maxlen; int max_depth; int max_breadth; virtual char avance(kmer_type graine, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer) = 0; void mark_extensions(set *extensions_to_mark); public: Traversal(Bloom *given_bloom, Set *given_debloom, Terminator *given_terminator) : bloom(given_bloom), debloom(given_debloom), terminator(given_terminator), maxlen(1000000),max_depth(500),max_breadth(20), stats(TraversalStats()), final_stats(TraversalStats()) { } ~Traversal(); void set_maxlen(int); void set_max_depth(int); void set_max_breadth(int); int traverse(kmer_type starting_kmer, char* resulting_sequence, int current_strand, kmer_type previous_kmer = 0); // n-order extension function, to ignore tips int extensions(kmer_type kmer, int strand, int &nt); // useful atomic avance functions int simple_paths_avance(kmer_type graine, int current_strand, bool first_extension, char * newNT); char random_unmarked_avance(kmer_type graine, int current_strand, bool first_extension, char * newNT); // high level starting kmer selection bool get_new_starting_node(kmer_type branching_kmer, kmer_type &starting_kmer); bool get_new_starting_node_improved(kmer_type branching_kmer, kmer_type &starting_kmer); bool find_starting_kmer_inside_simple_path(kmer_type kmer, kmer_type &starting_kmer); // now unused vector > bubbles_positions; // record the start/end positions of traversed bubbles (only from the latest traverse() call) TraversalStats final_stats, stats; void commit_stats(); // save current stats into final stats void revert_stats(); // discard changes in stats (because discarded contig) }; // n-order extension function, to ignore tips extern int order; // declared and initialized in assemb.cpp int traversal_extensions(kmer_type kmer, int strand, int &nt, Bloom *bloom_solid_kmers, Set *debloom); bool is_tip(kmer_type kmer, Bloom *bloom_solid_kmers, Set *debloom); class RandomBranchingTraversal: public Traversal { protected: char avance(kmer_type graine, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer); public: RandomBranchingTraversal(Bloom *given_bloom, Set *given_debloom, Terminator *given_terminator) : Traversal(given_bloom,given_debloom,given_terminator) {} }; class SimplePathsTraversal: public Traversal { char avance(kmer_type graine, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer); public: SimplePathsTraversal(Bloom *given_bloom, Set *given_debloom, Terminator *given_terminator) : Traversal(given_bloom,given_debloom,given_terminator) {} }; // auxiliary class that is used by MonumentTraversal and deblooming class Frontline { kmer_type starting_kmer; int starting_strand; Bloom *bloom; Set *debloom; Terminator *terminator; set *all_involved_extensions; kmer_type previous_kmer; queue_nodes frontline; // set already_frontlined; set already_frontlined; // making it simpler now public: bool check_in_branching; int depth; Frontline(kmer_type starting_kmer, int starting_strand, Bloom *bloom, Set *debloom, Terminator *terminator, set *all_involved_extensions, kmer_type previous_kmer = 0, bool check_in_branching = true); bool go_next_depth(); int size(); node front(); bool check_inbranching(kmer_type from_kmer, int current_strand); enum reason { NONE, ALREADY_FRONTLINED, IN_BRANCHING_DEPTH, IN_BRANCHING_BREADTH, IN_BRANCHING_OTHER, MARKED }; reason stopped_reason; }; class MonumentTraversal: public Traversal { //int max_length_deadend = 150; // replaced by sizeKmer+1 static const int consensuses_identity = 90; // traversing bubble if paths are all pair-wise identical by > 90% char avance(kmer_type graine, int current_strand, bool first_extension, char * newNT, kmer_type previous_kmer); set all_consensuses_between(kmer_type start_kmer, int start_strand, kmer_type end_kmer, int end_strand, int traversal_depth, set used_kmers, string current_consensus, bool &success); set all_consensuses_between(kmer_type start_kmer, int start_strand, kmer_type end_kmer, int end_node, int traversal_depth, bool &success); int find_end_of_branching(kmer_type starting_kmer, int starting_strand, kmer_type &end_kmer, int &end_strand, kmer_type previous_kmer, set *all_involved_extensions); bool explore_branching(kmer_type start_kmer, int start_strand, char *consensus, int &consensus_length, kmer_type previous_kmer); bool explore_branching(kmer_type start_kmer, int start_strand, char *consensus, int &consensus_length, kmer_type previous_kmer, set *all_involved_extensions); bool validate_consensuses(set consensuses, char *result, int &result_length); public: static bool all_consensuses_almost_identical(set consensuses); MonumentTraversal(Bloom *given_bloom, Set *given_debloom, Terminator *given_terminator) : Traversal(given_bloom,given_debloom,given_terminator) {} bool find_starting_kmer(kmer_type kmer, kmer_type &starting_kmer); }; #endif kissplice-2.4.0-p1/debruijn-v4/minia/Utils.cpp000644 000765 000024 00000050420 12676004527 021164 0ustar00ishistaff000000 000000 #include "Utils.h" #include "Bank.h" // some globals that don't really belong anywhere int nks; // min abundance uint32_t max_couv = 2147483646; // note: uint_abundance_t is 32 bits in SortingCount.cpp struct timeval tim; uint64_t nbkmers_solid = 0, b1_size = 0; const char *solid_kmers_file = (char *)"solid_kmers_binary"; const char *false_positive_kmers_file = (char *)"false_positive_kmers"; const char *bloom_file = (char *)"bloom_data"; const char *assembly_file = (char *)"contigs.fa"; const char *branching_kmers_file = (char *)"branching_kmers"; // (only useful for multiple assemblies with same bloom&debloom structure (ie debugging)) const char *binary_read_file = (char *)"reads_binary"; const char *histo_file_name = (char *)"histo"; const char *breakpoints_file_name = (char *)"breakpoints"; const char *assoc_kmer_file = (char *)"paired_kmer"; // prefix-based output files naming char prefix[1024]; char fileName[1024]; char *return_file_name(const char *suffix) { if (strlen(prefix)>0) sprintf(fileName,"%s.%s",prefix,suffix); else sprintf(fileName,"%s",suffix); return fileName; } int readlen; #ifndef NO_BLOOM_UTILS template // T can be Bloom, BloomCpt, BloomCpt3 or LinearCounter (just needs to support add(kmer_type) and possibly contains(kmer_type)) // U can be BloomCpt or BloomCpt3 void bloom_pass_reads(Bank *Sequences, T *bloom_to_insert, U *bloom_counter, char *stderr_message) { int64_t NbRead = 0; int64_t NbInsertedKmers = 0; Sequences->rewind_all(); char * rseq; long i; kmer_type kmer, graine, graine_revcomp; while (Sequences->get_next_seq(&rseq,&readlen)) { for (i=0; icontains_n_occ(kmer,nks)) continue; } bloom_to_insert->add(kmer); NbInsertedKmers++; } NbRead++; if ((NbRead%10000)==0) fprintf (stderr,stderr_message,13,NbRead); } // fprintf (stderr,"\nInserted %lld %s kmers in the bloom structure.\n",(long long)NbInsertedKmers,"(redundant)"); } template // T can be Bloom, BloomCpt or BloomCpt3 void bloom_pass_reads_binary(T *bloom_to_insert, BloomCpt *bloom_counter, char *stderr_message) { //fprintf(stderr,"binary pass \n"); int64_t NbRead = 0; int64_t NbInsertedKmers = 0; kmer_type kmer; // read solid kmers from disk BinaryBank * SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer),0); while(SolidKmers->read_element(&kmer)) { // printf("kmer %lld\n",kmer); bloom_to_insert->add(kmer); NbInsertedKmers++; NbRead++; if ((NbRead%10000)==0) fprintf (stderr,stderr_message,13,(long long)NbRead); } // fprintf (stderr,"\nInserted %lld %s kmers in the bloom structure.\n",(long long)NbInsertedKmers,"solid"); SolidKmers->close(); } int estimated_BL1; uint64_t estimated_BL1_freesize; float NBITS_PER_KMER = 11 ; // number of bits per kmer that optimizes bloo1 size // loading bloom from disk template //bloocpt or bloocpt3 Bloom *bloom_create_bloo1(T *bloom_counter, bool from_dump) { BinaryBank * SolidKmers ; if(from_dump && nsolids) // from dump and known number of solid kmers { //nsolids is sotred in a config file //number of solid kmers cannot be computed precisely from bloom file, imprecision of 0-7 estimated_BL1 = max( (int)ceilf(log2f(nsolids*NBITS_PER_KMER)), 1); estimated_BL1_freesize = (uint64_t)(nsolids*NBITS_PER_KMER); b1_size = (uint64_t) estimated_BL1_freesize; nbkmers_solid = nsolids ; // for correct printf in print_size_summary printf("load bloom from dump, containing %lli solid kmers b1_size %lli\n",nsolids,b1_size); } else { // get true number of solid kmers, in order to precisely size the bloom filter SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer_type),0); estimated_BL1 = max( (int)ceilf(log2f(SolidKmers->nb_elements()*NBITS_PER_KMER)), 1); estimated_BL1_freesize = (uint64_t)(SolidKmers->nb_elements()*NBITS_PER_KMER); //printf("nelem %lli nbits %g \n",(long long)(SolidKmers->nb_elements()),NBITS_PER_KMER); } //printf("Allocating %0.1f MB of memory for the main Bloom structure (%g bits/kmer)\n",(1LL<set_number_of_hash_func((int)floorf(0.7*NBITS_PER_KMER)); if (from_dump) bloo1->load(return_file_name(bloom_file)); // just load the dump else { bloom_pass_reads_binary(bloo1, bloom_counter, (char*)"%cInsert solid Kmers in Bloom %lld"); // use the method reading SolidKmers binary file, was useful when varying Bloom size (!= dumped size) //bloo1->dump(return_file_name(bloom_file)); // create bloom dump SolidKmers->close(); } return bloo1; } // wrapper for default behavior: don't load from dump template //bloocpt or bloocpt3 Bloom *bloom_create_bloo1(T *bloom_counter) { return bloom_create_bloo1(bloom_counter, false); } template Bloom *bloom_create_bloo1(BloomCpt *bloom_counter); // trick to avoid linker errors: http://www.parashift.com/c++-faq-lite/templates.html#faq-35.13 template Bloom *bloom_create_bloo1(BloomCpt *bloom_counter, bool from_dump); // -------------------------------------------------------------------------------- // below this line: unused kmer counting code FILE * F_kmercpt_read; FILE * F_kmercpt_write; //in last partition : create solid kmers file, and load solid kmers in bloo1 bloom void end_kmer_count_partition(bool last_partition, Hash16 *hasht1) { int value; int cptk=0; int64_t nso=0; /////////////////////////begin write files rewind (F_kmercpt_read); rewind (F_kmercpt_write); #ifndef MINGW ftruncate(fileno(F_kmercpt_write), 0); //erase previous file #else // tempfix? fileno is not accepted by mingw fclose(F_kmercpt_write); F_kmercpt_write = fopen("kmer_count2","wb+"); #endif BinaryBank * SolidKmers = NULL; kmer_type graine; if (last_partition) SolidKmers = new BinaryBank(return_file_name(solid_kmers_file),sizeof(kmer_type),1); while(fread(&graine, sizeof(graine),1, F_kmercpt_read)){ fread(&cptk, sizeof(cptk), 1, F_kmercpt_read); hasht1->remove(graine,&value); // if graine is present, get value of graine and remove graine, else value=0 cptk += value; fwrite(&graine, sizeof(graine), 1, F_kmercpt_write); fwrite(&cptk, sizeof(cptk), 1, F_kmercpt_write); if (last_partition && cptk >= nks) // if last partition, also need to search for solid kmers in remaining of hasht1, so this is not enough: { SolidKmers->write_element(&graine); nso++; } } hasht1->dump(F_kmercpt_write); // dump remaining of hasht1 if (last_partition) { nso+=hasht1->getsolids(NULL,SolidKmers,nks); // get remaining solids of hasht1 fprintf(stderr,"nsolid kmers = %lli \n",(long long)nso); SolidKmers->close(); #ifndef MINGW ftruncate(fileno(F_kmercpt_read), 0); //erase previous file #else // tempfix? fileno is not accepted by mingw fclose(F_kmercpt_read); F_kmercpt_read = fopen("kmer_count2","wb+"); #endif } } template void exact_kmer_count(Bank *Sequences, T *bloom_counter, unsigned long max_memory) { FILE * count_file = fopen("kmer_count","wb+"); FILE * count_file_2 = fopen("kmer_count2","wb+"); FILE * F_tmp; F_kmercpt_read = count_file ; F_kmercpt_write = count_file_2; Sequences->rewind_all(); unsigned int max_kmer_per_part = max_memory*1024LL*1024LL /sizeof(cell); int numpart = 0; char * rseq; long i; int64_t NbRead = 0; int64_t NbInserted = 0; int64_t NbInserted_unique = 0; kmer_type kmer, graine, graine_revcomp; // that code makes hasht1 occupy full memory. should probably be reduced (but we're deprecating that procedure, right?) int NBITS_HT = max( (int)ceilf(log2f((max_memory*1024L*1024L)/sizeof(cell_ptr_t))), 1); Hash16 *hasht1 =new Hash16(NBITS_HT); // partitioned exact kmer counting based on Bloom filter for solidity: // the bloom filter enables membership test for a set S of supposedly solid kmers (contains false positives) // read the (redundant) kmers from the reads, and load only those in S, in chunks, into a hash table // at each pass, update a file containing the true count of non-redundant supposedly solid kmers (S) // at the end, analyze the file to keep only those with true count >= solid while (Sequences->get_next_seq(&rseq,&readlen)) { for (i=0; icontains_n_occ(kmer,nks)) continue; //insert into hasht1 NbInserted_unique += hasht1->add(kmer); NbInserted++; if(hasht1->nb_elem >max_kmer_per_part) //end partition { fprintf(stderr,"End of Kmer count partition %lli / %i \n",(long long)(hasht1->nb_elem),max_kmer_per_part); if(numpart==0) hasht1->dump(F_kmercpt_write); else end_kmer_count_partition(false,hasht1); //swap file pointers F_tmp = F_kmercpt_read; F_kmercpt_read = F_kmercpt_write; F_kmercpt_write = F_tmp; /////////end write files //reset hash table hasht1->empty_all(); numpart++; } ///end partition } NbRead++; if ((NbRead%10000)==0) fprintf (stderr,"%cLoop through reads for exact kmer count %lld",13,(long long)NbRead); } fprintf (stderr," \nTotal Inserted in hash (ie output of Bloom) unique %lli / %lli redundants \n",(long long)NbInserted_unique,(long long)NbInserted); ///////////////////////// last partition end_kmer_count_partition(true,hasht1); delete hasht1; } void bloom_count(Bank *Reads, unsigned long max_memory) { #define NBITS_BLOOMCPT 23 // 33 :4GB (4 bits/elem) // size of the bloom counter table to count kmers fprintf(stderr,"nbits bloom counter: %i \n",NBITS_BLOOMCPT); BloomCpt3 * bloocpt = new BloomCpt3(NBITS_BLOOMCPT); BloomCpt3 * bloocpt2 = new BloomCpt3(NBITS_BLOOMCPT); bloocpt->setSeed( 0x4909FEA3A68CC6A7LL); bloocpt2->setSeed( 0x0CD5DA28467C5492LL); // bloocpt->set_number_of_hash_func(4); // bloocpt2->set_number_of_hash_func(6); ///////////////////////////////////first pass ; count kmers with Bloom cpt bloom_pass_reads(Reads,bloocpt, (BloomCpt * ) NULL, (char*)"%cFirst pass %lld"); fprintf (stderr,"\n ____________ Second bloom counter _________\n"); bloom_pass_reads(Reads,bloocpt2, bloocpt, (char*)"%cSecond pass %lld"); STARTWALL(count); fprintf(stderr,"\n------------------ second pass bloom counter \n\n"); delete bloocpt; ////////////////////////////////////// exact kmer count with hash table partitionning, //also create solid kmers file and fills bloo1 exact_kmer_count(Reads,bloocpt2,max_memory); ////////////////////////////////////// STOPWALL(count,"Counted kmers"); fprintf(stderr,"\n------------------ Counted kmers and kept those with abundance >=%i \n\n",nks); ////////////////////////////////////////////////////fin bloom insert //delete bloocpt2; } void estimate_distinct_kmers(unsigned long genome_size, Bank *Reads) { int size_linearCounter = genome_size * 8; // alloc 8 bits * genome size for counting, i.e. ~ as much as the assembly Bloom size LinearCounter *linearCounter = new LinearCounter(size_linearCounter); bloom_pass_reads(Reads,linearCounter, (BloomCpt * ) NULL, (char*)"%cEstimating number of distinct kmers (%lld reads processed so far)"); long nb_distinct_kmers = linearCounter->count(); if (linearCounter->is_accurate()) printf("Estimated that %ld distinct kmers are in the reads\n",nb_distinct_kmers); else printf("Cannot estimate the number of distinct kmers. Allocate a larger counter\n"); delete linearCounter; } uint64_t extrapolate_distinct_kmers(Bank *Reads) { // start with 100MB RAM estimation and grow if necessary return extrapolate_distinct_kmers_wrapped(100000000L, Reads); } uint64_t extrapolate_distinct_kmers_wrapped(unsigned long nbytes_memory, Bank *Reads) { unsigned long size_linearCounter = nbytes_memory * 8L; // alloc 8 bits * nbytes for counting LinearCounter *linearCounter = new LinearCounter(size_linearCounter); int stops = 100000; // variant of bloom_pass_reads int64_t NbRead = 0; int64_t NbInsertedKmers = 0; Reads->rewind_all(); char * rseq; long i; kmer_type kmer, graine, graine_revcomp; long nb_distinct_kmers = 0; long previous_nb_distinct_kmers = 0; uint64_t estimated_nb_reads = Reads->estimate_nb_reads(); bool stop = false; while (Reads->get_next_seq(&rseq,&readlen)) { if (stop) break; for (i=0; iadd(kmer); NbInsertedKmers++; if (NbInsertedKmers % stops == 0 && NbRead != 0) { previous_nb_distinct_kmers = nb_distinct_kmers; nb_distinct_kmers = linearCounter->count()*estimated_nb_reads/NbRead; //printf("estimated now: %ld\n",nb_distinct_kmers); // the following condition will grossly over-estimate the number of distinct kmers // I expect the correct result to be in the same order of magnitude if (abs((int)(nb_distinct_kmers-previous_nb_distinct_kmers)) < previous_nb_distinct_kmers/20) // 5% error stop = true; if (!linearCounter->is_accurate()) stop = true; } } NbRead++; if ((NbRead%10000)==0) fprintf (stderr,(char*)"%cExtrapolating number of distinct kmers %lld",13,NbRead); } if (!linearCounter->is_accurate()) { printf("Inaccurate estimation, restarting with %lu MB RAM\n",(2*nbytes_memory)/1024/1024); delete linearCounter; return extrapolate_distinct_kmers_wrapped(2*nbytes_memory, Reads); } nb_distinct_kmers = linearCounter->count()*estimated_nb_reads/NbRead; // this is a very rough estimation printf("Linear estimation: ~%ld M distinct %d-mers are in the reads\n",nb_distinct_kmers/1000000L,sizeKmer); delete linearCounter; return nb_distinct_kmers; } #endif float needleman_wunch(string a, string b) { float gap_score = -5; float mismatch_score = -5; float match_score = 10; #define nw_score(x,y) ( (x == y) ? match_score : mismatch_score ) int n_a = a.length(), n_b = b.length(); float ** score = (float **) malloc (sizeof(float*) * (n_a+1)); for (int ii=0; ii<(n_a+1); ii++) { score [ii] = (float *) malloc (sizeof(float) * (n_b+1)); } // float score[n_a+1][n_b+1]; //stack is too small // float pointer[n_a+1][n_b+1]; for (int i = 0; i <= n_a; i++) score[i][0] = gap_score * i; for (int j = 0; j <= n_b; j++) score[0][j] = gap_score * j; // compute dp for (int i = 1; i <= n_a; i++) { for (int j = 1; j <= n_b; j++) { float match = score[i - 1][j - 1] + nw_score(a[i-1],b[j-1]); float del = score[i - 1][j] + gap_score; float insert = score[i][j - 1] + gap_score; score[i][j] = max( max(match, del), insert); } } // traceback int i=n_a, j=n_b; float identity = 0; while (i > 0 && j > 0) { float score_current = score[i][j], score_diagonal = score[i-1][j-1], score_up = score[i][j-1], score_left = score[i-1][j]; if (score_current == score_diagonal + nw_score(a[i-1], b[j-1])) { if (a[i-1]== b[j-1]) identity++; i -= 1; j -= 1; } else { if (score_current == score_left + gap_score) i -= 1; else if (score_current == score_up + gap_score) j -= 1; } } identity /= max( n_a, n_b); // modif GR 27/09/2013 max of two sizes, otherwise free gaps for (int ii=0; ii<(n_a+1); ii++) { free (score [ii]); } free(score); //printf("---nw----\n%s\n%s -> %.2f\n--------\n",a.c_str(),b.c_str(),identity); return identity; } void Progress::init(uint64_t ntasks, const char * msg) { gettimeofday(×tamp, NULL); heure_debut = timestamp.tv_sec +(timestamp.tv_usec/1000000.0); fprintf(stderr,"| %-*s |\n",98,msg); todo= ntasks; done = 0; partial =0; for (int ii=0; ii<16;ii++) partial_threaded[ii]=0; for (int ii=0; ii<16;ii++) done_threaded[ii]=0; subdiv= 100; steps = (double)todo / (double)subdiv; if(!timer_mode) { fprintf(stderr,"[");fflush(stderr); } } void Progress::finish() { set(todo); if(timer_mode) fprintf(stderr,"\n"); else fprintf(stderr,"]\n"); fflush(stderr); todo= 0; done = 0; partial =0; } void Progress::finish_threaded()// called by only one of the threads { done = 0; double rem = 0; for (int ii=0; ii<16;ii++) done += (done_threaded[ii] ); for (int ii=0; ii<16;ii++) partial += (partial_threaded[ii] ); finish(); } void Progress::inc(uint64_t ntasks_done, int tid) { partial_threaded[tid] += ntasks_done; done_threaded[tid] += ntasks_done; while(partial_threaded[tid] >= steps) { if(timer_mode) { struct timeval timet; double now; gettimeofday(&timet, NULL); now = timet.tv_sec +(timet.tv_usec/1000000.0); uint64_t total_done = 0; for (int ii=0; ii<16;ii++) total_done += (done_threaded[ii] ); double elapsed = now - heure_debut; double speed = total_done / elapsed; double rem = (todo-total_done) / speed; if(total_done > todo) rem =0; int min_e = (int)(elapsed / 60) ; elapsed -= min_e*60; int min_r = (int)(rem / 60) ; rem -= min_r*60; fprintf(stderr,"%c%-5.3g %% elapsed: %6i min %-4.0f sec estimated remaining: %6i min %-4.0f sec ",13,100*(double)total_done/todo,min_e,elapsed,min_r,rem); } else { fprintf(stderr,"-");fflush(stderr); } partial_threaded[tid] -= steps; } } void Progress::inc(uint64_t ntasks_done) { done += ntasks_done; partial += ntasks_done; while(partial >= steps) { if(timer_mode) { gettimeofday(×tamp, NULL); heure_actuelle = timestamp.tv_sec +(timestamp.tv_usec/1000000.0); double elapsed = heure_actuelle - heure_debut; double speed = done / elapsed; double rem = (todo-done) / speed; if(done>todo) rem=0; int min_e = (int)(elapsed / 60) ; elapsed -= min_e*60; int min_r = (int)(rem / 60) ; rem -= min_r*60; fprintf(stderr,"%c%-5.3g %% elapsed: %6i min %-4.0f sec estimated remaining: %6i min %-4.0f sec ",13,100*(double)done/todo,min_e,elapsed,min_r,rem); } else { fprintf(stderr,"-");fflush(stderr); } partial -= steps; } } void Progress::set(uint64_t ntasks_done) { if(ntasks_done > done) inc(ntasks_done-done); } kissplice-2.4.0-p1/debruijn-v4/minia/Utils.h000644 000765 000024 00000006111 12676004527 020627 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include #include // for log2f #include // for max #include // for truncate #include // for INT_MAX #ifndef UTILS_H #define UTILS_H #include "Bank.h" #include "Kmer.h" #ifndef NO_BLOOM_UTILS #include "Bloom.h" #include "Hash16.h" #include "LinearCounter.h" #endif using namespace std; # extern struct timeval tim; #define STARTWALL(TT) \ gettimeofday(&tim, NULL);\ double wdebut ## TT =tim.tv_sec +(tim.tv_usec/1000000.0); #define STOPWALL(TT,MESSAGE) \ gettimeofday(&tim, NULL);\ double wfin ## TT =tim.tv_sec +(tim.tv_usec/1000000.0); \ fprintf(stderr,"-------------------%s time Wallclock %g s\n",MESSAGE, wfin ## TT-wdebut ## TT ); // global variables extern int nks; extern uint32_t max_couv; extern char prefix[1024]; extern float NBITS_PER_KMER; // constants extern const char *solid_kmers_file;// = (char *)"solid_kmers_binary"; extern const char *false_positive_kmers_file ;//= (char *)"false_positive_kmers"; extern const char *bloom_file ;//= (char *)"bloom_data"; extern const char *assembly_file ;//= (char *)"contigs.fa"; extern const char *branching_kmers_file ;//= (char *)"branching_kmers"; // (only useful for multiple assemblies with same bloom&debloom structure (ie debugging)) extern const char *binary_read_file;// = (char *)"reads_binary"; extern const char *histo_file_name ;//= (char *)"histo"; extern const char *breakpoints_file_name; // = (char *)"breakpoints"; extern const char *assoc_kmer_file ; char *return_file_name(const char *suffix); void estimate_distinct_kmers(unsigned long genome_size, Bank *Reads); uint64_t extrapolate_distinct_kmers(Bank *Reads); uint64_t extrapolate_distinct_kmers_wrapped(unsigned long nbytes_memory, Bank *Reads); #ifndef NO_BLOOM_UTILS void bloom_count(Bank * reads, unsigned long max_memory); template Bloom *bloom_create_bloo1(T *bloom_counter); template Bloom *bloom_create_bloo1(T *bloom_counter, bool from_dump); templatevoid bloom_pass_reads_binary(T *bloom_to_insert,BloomCpt *bloom_counter,char *stderr_message); templatevoid bloom_pass_reads(Bank *Sequences,T *bloom_to_insert,U *bloom_counter,char *stderr_message); #endif float needleman_wunch(string a, string b); class Progress { public: int timer_mode; struct timeval timestamp; double heure_debut, heure_actuelle ; uint64_t done; uint64_t todo; int subdiv ; // progress printed every 1/subdiv of total to do double partial; double partial_threaded[16]; uint64_t done_threaded[16]; double steps ; //steps = todo/subidv void init(uint64_t ntasks, const char * msg); void finish(); void finish_threaded();// called by only one of the threads void inc(uint64_t ntasks_done); void inc(uint64_t ntasks_done, int tid); //threads collaborate to this same progress bar void set(uint64_t ntasks_done); Progress () : timer_mode(0) {} //include timer, to print ETA ? }; #endif kissplice-2.4.0-p1/doc/CMakeLists.txt000644 000765 000024 00000000471 12575016362 017436 0ustar00ishistaff000000 000000 # Uncomment the following 2 lines to compile the user guide at build-time # (requires pdflatex) #FIND_PACKAGE(LATEX REQUIRED) #ADD_CUSTOM_TARGET(user_guide.pdf ALL ${PDFLATEX_COMPILER} "${PROJECT_SOURCE_DIR}/doc/user_guide.tex") # Install user guide install(FILES user_guide.pdf DESTINATION share/doc/kissplice) kissplice-2.4.0-p1/doc/kissplice.1000644 000765 000024 00000004363 12575016362 016752 0ustar00ishistaff000000 000000 .TH KISSPLICE "1" "June 2013" "kissplice 1.8.3-1" "User Commands" .SH NAME kissplice \- detection of polymorphisms in RNA-seq data. .SH SYNOPSIS kissplice \-h .br kissplice [OPTION] [\-r READFILES] .SH DESCRIPTION Detects alternative splicing events and other kinds of polymorphisms from READFILES (in FASTA or FASTQ format). .SH OPTIONS .TP .B \-h, \-\-help Show this help message and exit. .TP \fB\-r\fR READFILES Input fasta/q read files (multiple, such as "\-r file1 \fB\-r\fR file2..."). .TP \fB\-k\fR KVAL k\-mer size (default=25). .TP \fB\-l\fR LLMAX Maximal length of the shorter path (default: 2k\-1). .TP \fB\-m\fR LL_MIN Minimum length of the shorter path (default 2k\-8). .TP \fB\-M\fR UL_MAX Maximum length of the longest path (default 1000), skipped exons longer than UL_MAX are not reported. .TP \fB\-g\fR GRAPH_PREFIX Path and prefix to pre\-built de Bruijn graph (suffixed by .edges/.nodes) if jointly used with \fB\-r\fR, graph used to find bubbles and reads used for quantification. .TP \fB\-o\fR OUT_DIR Path to store the results (default = ./results). .TP \fB\-d\fR PATH_TO_TMP Specific directory (absolute path) where to build temporary files (default temporary directory otherwise). .TP \fB\-t\fR NBPROCS Number of cores (must be <= number of physical cores). .TP \fB\-s\fR Don't output SNPs (saves time). .TP \fB\-v\fR Verbose mode. .TP \fB\-u\fR Keep the nodes/edges file for unfinished bccs. .TP \fB\-c\fR MIN_COV Discard k\-mers tha are present strictly less than this number of times in the dataset. (default 2). .TP \fB\-C\fR MIN_RELATIVE_COV Discard edges with relative coverage below MIN_RELATIVE_COV expressed as a percentage in [0,1). (default 0.02). .TP \fB\-z\fR GENOME_SIZE Estimated number of nodes in the De-Bruin Graph. (default = 1000000000). .TP \fB\-e\fR MIN_EDIT_DIST Classify as inexact repeats those bubbles whose paths' edit distance is smaller than MIN_EDIT_DIST (default 3). .TP \fB\-y\fR MAX_CYCLES Maximal number of bubble enumerations in each bcc. If exceeded, no bubble is output for the bcc (default 10000). .TP \fB\-\-timeout\fR TIMEOUT Max amount of time (in seconds) spent for enumerating bubbles in each bcc. If exceeded, no bubble is output for the bcc (default 900). .TP \fB\-\-version\fR Display program's version number and exit. kissplice-2.4.0-p1/doc/user_guide.pdf000644 000765 000024 00002170101 12703734560 017524 0ustar00ishistaff000000 000000 %PDF-1.5 % 3 0 obj << /Length 1025 /Filter /FlateDecode >> stream xYKs6WVp&D~c8MkO2=pJ L}HٲIǎ|1L ,>,o"*!rK{:;y;5 #X)&2abmUĄdH>?,.{,EF2ɘ`˪:e ]L:81heQC^vÜiQU]N,Z k-z *k0"&I[LLк6Xb^bu:d7n*0k@¼b_mS/?P9KƌJ Kg89A M-yB)R+@@afh0q=R}';KqZKw ͪ+uЈd5c3TXaņqh[/Soʙw&CO|IskKmOy38@!b0LaJB(:sgo´p޳aZjLr `6B1cDD$%7cwn隀dz1(?"R-| I1hˇ4 dlL(#UہbD{1I ԰,ݎ0rO R/U^>XI^BbkwψO5K CyoJ^Юa tk3>`o!WX1 #u>`Uh4}+u'1I.6ԍf*Mb ~(՛@)}Be 8 ե/_dn\P-7?:}H R//IDIΫM﯒HF!d-_oys}APYkWk<^Gv1S7 Rc~-6;m endstream endobj 15 0 obj << /Length 129 /Filter /FlateDecode >> stream xU; 1>bˤHnk6V0u:|4'( !akf 1A;AƠ"#8_Plueeq󜓽/4_o<)*v_Q~mb ^D%uly % endstream endobj 18 0 obj << /Length 2290 /Filter /FlateDecode >> stream xڥXIsW&NϖcѳT^99@2PſuO@Tv|=~݃[y(p{0fe91w^Z[ttĺ 8IӢ;B7FlkoVpyֶLݔ6(wo^fVC)@T1)_RA;L"n&?Y kY/3;rhUH!(q%cӳsGfi=8׈C2`= 1~O&RI=KYA "5;=Koْw4ص( _6.2`M91 uh:uvYei-}69yO3D;lkC:8s,5萓DfgW3ڧ ="bkemrgtg+¿g,'e:>ȭԻ^9/{dWitӸy{䉦UMPBO,ojt<4bo_NL\2$&v6H}K'IGNW_6H;(M-M`Bᾀ" i8ۋS[SjY7`/G&Aȧn UT$iL W6na!~vÀe$&I8@eV:W6Ӡ6 K7ovvæ>qݴycn(u\Wv&6p8 7ƟbZ1zkwuOl43Uc2+f9 E([͎^Caۘ*^d"q w$s2% z\baJc"_9,T &n[^88tT_PߴSZJ9%vCLUQg[eJƌgpP9֏H+ i[v[W 񩧏c{$ KM_ħ4<*GQ"1`Ku`Ӌ$U(GػٙC5@C[譸4g SKfS`^X})BC)ЃЖd: =n]H#6S"#CӒ Fc ī Ɣ7# S&6u~ؙ*i lP-Neؕ'Dsȱba2@;RĻeoX_A n' l\Ghtݲ&nB]# =0lc!EPiۭR l^+?,+4((H8&ҐNJofF ՟xb> stream xڭY[s۶~#5č :Ӈ$MZ>X9gr:ZlN(Q%?X6eZM? r J(~>J~-?4YEXEfܤI\>ǂ\ڟ>ϕM⟫\4.63a6GeU׮Ymfs״wUnV_Gx'x0.ydx²4&l%L,H)ΒD¸ΏD$&;#}AYBV1.a\2:,"$Yx<fV]UMhmY,ovP\/02vˏh:@О ?~pG;=VO)}^̡pPӗVM_+_/JWmsx_fZ9]fZr&,kt%^΢+y%gKP6{_y, e8)Rba u`Krl;(q?sM{((h1 ̦J ,:!tԷEJ.>2%fJLXBu"8 ct4m^5P: gT=,vf⓬r`P)ImH"{@!<эL'u Ȃ' aVZ7.syY|,&{D{vͼb+X$Bp|zVy|(N tĴκcoKpchy!Keg\miI%desD^LYo3'g狷|??̞F>ϗŅ"h;=rgP|"'6\7UoxCj}y}R+ցE B$dQ]cõwkW? r>2|،FX`$"nZ!\ hL4Y88Oh0~S@ gDsI;z,^̬g:g`;~+sn< M֓zƇw[=XV;(t1X9 $Mr!dמo <."4yhڔwteE0(:6"noWmRn*>E@yJc.+J@*vv׆E. W.X>9ycOq>w5MuAe=MQfګfZw(G<`Z[0[2Z=dl'-\1{%*{CAaH׹0}4JcA__"U5kayhl8 2>9w[䣀$O=iF%߂At7[i6qfi!cw{Uѡ4r[a#C.Tin#Aڹ"-A?p|-XƿC3 [J4%@ehF2|xSP]`A` @MT4jTX8.=~fm"r}Yee&~0 m킜}ٵpڅz601זL pjPMI {巑:'h >1JH\uc[KW9mS;$Sp S qqaշE Xs!̾ruz ՀZ* endstream endobj 33 0 obj << /Length 1937 /Filter /FlateDecode >> stream xڥXo6_!Ij>2[nֵkahD,4x,jO>$n8Bā,Z MY$uHR.>\\]h*5:A5]ig.DX%S`KEcnHa^W ))YBaM\EmObT0M4Xb7M tP2AĢ(&UTmd(zC猾[[1mg%bŒ,lc̤3LZϚ_11&$M+ROKHqhc~ݕEɒX j15{ Y*]ہ Vi4QFk~rS&*|pBgZ Y`[T 4EwKݘ}! (ܙl-Y<61<}$T5xEbkNw t A7<0d5ƶ lg1S벜3f9nw 5=GIg?]}k-3M"& HmRuQ&ql>)Үqq_M*=e\^"7̘> [:= ;t>NjgX|W3 $p~E}= `?[j R )J je_7 (gj6JFTԡ6鿫KӶv׮qm:(=] M1R*X"~`?4O҅,nѸ/!x2)m+~B_6OJcK:z>g;= p+]h 03񓞔 %Evq!jЏ)7V+!%Y>mFEq㼥CA/dy0^s@3C>##5Z2KǕ5vాm {ڂ1C?u^׮yhdm}Cl!_4T437~A OBA$ ehօv#|_敂@ƫ$ΦXg8Zqo)466G)fCRNY#wIeDqܷE/dX@R{:=nM"%xê෠s7D>p-Ǵ}>R| sF S?ajMl[dequI%NX 5iZEr|uA w,şعQ+3NAYݰL`r9R%G*ǥBw$+ 8_ TzXcfZ#N#;)UOp9J]1}4[:1'Niw!=] @'l@DO@$eT^4җ de2) a'58U:`LiF*­i; m ѡ+ h̐ܯU4Ul6 SG Pz@8`QÔļn wxZZc@-:QIw8hKN+?Qw endstream endobj 28 0 obj << /Type /XObject /Subtype /Image /Width 952 /Height 205 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 35 0 R /Length 84781 /Filter /FlateDecode >> stream x}w|EWs!@rwrwwPB !6P.*("`:ޥvvvfwgv璃}/޳<3}gWK&/=$W OtjXw˰-|Z_k% {e'DO@`r8T [s^K) ^THlRPf'.22 2suNNFVůFfNK=F@C jLCɈNh/[褦&q48lo4=OhbdbDNQ<O 1P2d`gϖFQOPlr¦Sد,DQy 5W&؃"( 'S1є1"B?Yrg|ŎVKa^<^U[BG%%d$@~ufH--5[F@ep!k*f' 89>VQ_-,s(قoepT#,c>2= r?k+ %^Z8w0 Y89) #WW;Cȉ5`Vr@sw͚6<24s,7;={ezwھvJ:쟐¤6%Ե˕58Fxq+WlYN9MicD&jw̤Ry.7˘m֪霝LoGv`~z̀IG||b6,8B|~hd]264yX_E cC~,ƙ w |2|P {:zї쒌hzoֈvc6]jZۛD$F]s2uAX=؞"A:he}lq c (`lyŲ֪=AU5.YKۛ aFOꌖko|$8=ȗ3-O '[sK~vCxzG:#'qLE6_=r-~Ru%̙,?99sEa B\;c9%Xed`YDn hw'8:H9!o?8е۫#|b0];"=4OfJN& T47$ObJjS+8'4'W=h1U169 yf NUqZr |g ByU-h'KOdY$:z#06?3놴 wwsyF9s]@]dBז|.cfnCW"au Ve\omZZ1W0Sl>?XAbf/m:Թ:O^Ci%SlR?e}rCbCnI{Mp 'GZɞymxH\#uo՛#)]'q2;6^p':6;>\QM"GCu|h] :&/ؔ&15Z>*K{}ɥBp n?]ʟ_'KA|a`or1muZk43֦!d=U4<ӛ|­MA&0,_?9?hN0x{ v@sf/[ m”ĕz?Dgqqɾ?'4R vzD]ZcC t5JlUլ{L}61xѮvddbqñ! !-7xUUJ '?>䶶fd"G Y3w/Kٽwn%#[EɎȡ :WpD  >UB?ylR'ׁC;Xݥ3LJ)efIX 闳2]Q&bWQ2N~cYп4ō11wq}l:ν{x^ նv0g#pW:\i=xp<IJr2\6Ů-[FxIbϋ?qeL@[5@~xNp3 Y~OVBxķ*ۻD>o'{=>򞵋Gvʮ-KrwkyD;/tma05NE` xZJ> m{`ʪq/BxDᖈ~=Ԅ6/Uۗ-yFfyTgbu A`ACֈČ6Dp-{p_,m !Аdyyܵ"kK7Gᘾ2a/#sGamr5,9"Ή>|C>yD ^^Te [u|1#qCwXrˎ 7'r^Qd-v IH3X({\mӳثQCϗ&vIxoWGUz%2Q2lnWy rDZbaVn;q?bw3^I@d7WBT!IOuy{J?䕍,IƤ.ػevp~|v@-cҾ߿yҵ׿+-I0A6I  YŮڒ *9&]WGxN>s~nRd0ui'SV#_%;ioǴ}"NF?YDAk/^#nڵhNve@T';/ 9NV аÁr87:>ܽ~ԩ'/wGqB3HH<'>z ?/<6qxV͏޵w@++'kW>oyC\X^kɻyه<}^"xĨoRѨ.^xٙm$]k{B/wp~H}4$|2k;^c.Xtk{d/o|f]d_ dFA`.y~^R׵2~Xu,S'8LIpGxR: zA?Z^DG[Fᣈ9V=PkA'gq3/sƒ?1̌kgO9z5Z$(prJH2_}jFbOvls`-zݨUmwlѦ?rZ p2ܩ蓚Vulknpsˑ8*cwRԅsuQQ$7' ]ҥoS3Ncҫ%{?4ӻkJvLXqC?wJN)׶ʔv/~iWZE6(Ln#[iMttd,G&b}rwuv} .G%y-1 28jfď wjb3A Ժ x]ien/n]ޞQd؀>cccg]d]XvO<$!k!A'v8yq򖑙5(R ۈP_P8dӈqNfd8Yco/@0ēP>c2vC{ )2 2JON6 <[{}-0sKΠ*dI~|`ii>#mx9eUV?9=7ӓҩk).]W5Y #ï,?7 W nB ql|ssDMھw̻#'m[rXֆv!mMyKzN+č *9ti'vܪ|{;=a9Gy>hמ.䝣K]OS/I988_N!pzvvrzurCzr~gѽ=5w3ԣkKv +Q/zB\2)*&~KwS!OU'5OWa6PƘ yp(N)(q4"k1jL| ﭣ83u.EBͩS8./uHh rmEf8U9'Le1Hy|Ռ8'o,KѻUSvG `{$ؾ.`ۗg32y{8ųm9w tl]pNB1 l_:o+üzKXk}WX]A0nG-.CrxwHW zHNQP>~2xL/gĖ?/PQl<-˻pbrCə6Фq/rrcu'gtx@WS-Yp>4@ַ><\!ۏ/XbAaJ:WOܵ\Kpt:ȠT@b"dw[pcِ¶z#.=:۲?~@W+'67b?hޅ̮]䀴o_ˇ& Λu!ί.z{.HcYu tV\3~&\g7`2,ep|5-']]WT;!UWZ Mu|GBPʇV:ƀ̃Tofƃ*^IQ^,N7oܲvK[9Ӫ/"j^.O|7캜[S$P#VcB-,OsVvHTedEچ<`+v+PYPUGNWX۷]'gpe@x2# ol8slil-Ep|.9Y~۸FLd)pQ܎䌎0ɑ.|28i˵-JL][4Ti\ԎVtjklJ1-=hxzCVr'$LC`Ud`"bCY<2wz.4Nk ItX< nx.#w5x3Z}3_kHʉnct-gYxnl][/=23p2xx<_w!W&/;xMȸ{EY5N|[;#ܻ&8Wp6WwbCy0I8TQCn]݉ 92&3В2<g, r;p.vzB.dcl '- h%iq0 0qoId'[xu$V4!V0ᨊ]RN 'tyEY—U,OdrE6ֻZP gǃM]bs=.XNY@M`,-U$|rkQ"Er%@i`Ї+\OXxdqaߌ +;j;i/9L!fԳp2ˣ\]2NcgmxƳ-Nt~ϯ/ib6y'?VՈ܂kwNa׆>b?vϨ#Ex3y> \rӼ &!/V_:o;_cuE[ s?=ʽ}(BC݇ɋe}]D\kgCI\MGǿ|ohަ_v=4mX:! C2q%UA\H,[LJf8]㓇vEt`]Yܚ,~jEN"mL ai\w#7=vbp2g@]r<\O]xOs9i~2 Ks&YZ'cZ\Y'LϹ¡ܰgjFemG~1YkGCX.1Ap6'dwY.n]\%-%Nv{[zg ]`%qna?r V'W:?{I*s89#`bVN ,u.ʝN83+ r Y0LRY9 {)VuHeG#r%իcؑcy!N•)?xpI;vVھ<|ݿbz(J}`2bWdBhOy/%O;?6_%{y2;.<&J[GdZ=~-a}gvJAWD6:|aë~Hx:@~Uo >tL` ;ɶ38ֱؘN1lП]rSPd]]c }=0< anFwc6էnδ^wN]@S:NeoҮ 9 pl)Nrgy ڙVa e<` .弮_ ' *~mG '/9 w;x^ d2ZG8rW. 0H&~2R s<9K`5[Onػ)[9:ZB(O'Ȟ}ԅChʛ}o=C:61yO)͠/ܼ;_^=k-Ŷaޙ g}9 7rsvE SjWXu|^rȰͿXΏZȞ0WBNocONF[PZĥN 7GwүgZ44{.V]Oa6wfUA:zae߹wy| k~!{ԪUˍ}~dM$wD }O2 Y}Fa[c8|/.M'-~wF |FljSd0󇕼4mծ!tgȠ,ZT6]oza_&b''8WCh'YP 239?#%JS?mn'L,S֩#QY?V]Nlj+yh;|ǫ(l}ҾXO /5wah7v57κpa~۷.>u'Sd"驳PHc/EH% uyN1 6q+qrjyc/g6l 4CD*O!:TB-!YsAl VO;LYRb\wCؽeӬsu`8FvFB:N#/WY6AoeyrڳqQA³ax0=b~5 W<{*<['>C8P4nٽ8Y+8٭񐇞%,ىYC̬p"j/ K7df,^˘qR[,5~.ѵ񖍉!Eu%!0ǥf]8:ѩNu(.mbCN@30,A sj}(4CJ۠ F E[[!qO5]DƓ3ҧ.Zeq3V|W޹m;cvCk۾s¿"I$1Ϡ4o/{p{u]<>xXe}v'IkeOV!ӀX:y[IJ{\},ֻGZ-2Z;9\ÍbV !{,TO`%>0,qDTФC0|`zF5Z*ibaTz~K;;Г a&3e HyW8zŪ44bŤT mڶFF*eSQh:6jUOdyCłT,_gXɩCRx]:> .Ʈ Y1vWܨݓ,u. G:†}Jø P]O?f(B*jfZcF52o +LRBKoq^!OLuBاE9 fHXg[X$Y'+D,ZR8p lPNQhK5 NS;\XP;*Hr4Se$`Tk~(g PG1 h 1>J0k4Gba ZێםPXfglgƃݛ<8JL a{,Bo ?ǣ} "[Wd񍕀/<ޮłl+zanyrLn̎g$WQWmEfmg}% ҽ'/׻0ҁ 1C?vর:@43cxrNR1[譌\ Ӽ%;'B1+gI욭 P8G pv1!G}xMuBmm{9'@EN8;:}KN^]9t m~>u煣5vuzfjC36[N !O̗o3pAWosFhbrA90ϖlBч*HvpΌ ?# /G pL9~ScAFu2d]8DBm:#u?>ŅTɡ>$N.sJ8zx]sx],omy:,uԙ7xNljm}lN hk%þzp{{7\wvGۊ#j$V"'iFV&_ނyAXYM <(B J_ZmkYɒ13C_S743ƣmM ĭTMmܛ$}[ 4vex$~|{Cwy+G<>PBm {RN .i̡֚cz̠rzkqr'حsqB6zTr$G1s '2Ʀm+1+c9ސ`/ēǩm1^Za<M: mU mII)ChW^iPz!Y\Wgϒu<^Ps*9]'_Δf}yFvql@}`ibivOt}_i][30_BU:\>@?5dk o$R柽F&8֘aID9> KnL?o7Գηde/8,dGXJyC_^r6I:39ԨBNɩmu2Y7@5C[gp<-oYnX  ;1Mf,5#rMAȶ,'> Uhף5C pvqJ])KSqH>ydNɖTL[NAc΢R<(Ng q iu>էQG8Nc,x& =oش-LF,q4>~ :Ʋ3JRkNoufA]گ<#N;E "-u8%OəE+a[;mu3X\ȡ?>:[0Uxc>̎~jgBǖ&YخC_]Vx a;fMby(ƆZikj~?8E񸺈<϶XeJŹ&Ю-ϷY ̢GIr?3DKsWa!m0ֲPyD GmTwt N0X-u"NNI.7gAioKM,~lme(۠k-ԋ-qS+~?e/R"KxtELKfW <:%OGc*Nf4ASAGͫYDtfrl{b8/l,OD4<4mi\-p#lk5dX&sb0Ĵ q~qvb>?wJy~hڶfLdN_mS4.~h~2Y+ ) '+!nހv]8M,Gvlbzҳ4}œ.b"Jt]րyFlRzfmUk4Nā2~67ʁ㙽.`DBG1!dy[8\}r0x9cGZ2N\(1@HqN( 5Vw!8GCg';I'ȟ:H8-&553Njn26NIn&F!f~ Vq_EtK׃Ќ>q7[K=%<&t/+G׎skR#6P:.RЮO\t_A,!LVX,,m+X}k8,*bcOFxk|(ڞNQS-8l17q%VᐶE̐%QGk~&lUT+UPG <<4mi\-przBV7MUO& C)N"p'$~rDmw/ǓUO,%l(}{]4P.r/ݗcccy-\S \)GˇnǕ_U} wU^쐣]Ї= }#JdБ~dg{coEbyP8 'LR|se:cp"NLO89\],t6J S[&OKwWДKSq g]^4򐰴67XiC!DŽ9c`}2F5Sx <udr93r(u0xJaPڥSY^W89/b} -FCE[]?RXx#!qfбsl`yCx.9OT"ɒ*m+Q_1`j)v)lGCwAlp'|Iq?O`Č q6Y0+sݶ (1M[>#dv|zlhUl!A~خ <~H琛L.74r2cMY [A/jVwsip2|BT"|eP› 8$GW R_tVdME<+;ǒ1<~aa\q^ 4VT`T,͉}uUN$ĥrsxߔ"[ < |k+ִ3drG#ZcB g AQޮߠ]ʿS9VVF Tp--vA}t}utu|8ՙK(N/p0^:B>;Ĝs"Oc v<+b\Շ GZٖQʃԙgC (#Na;Cb{`iMxQ#(as&';CO~χmi's6vKLC lׂlksYWuepgxQK{Ylx۞F!vv[a|ro.s9Yb/ܳMvWDõlkY_=[{F qhނ=NDy),N{Rז6 Y0\r $E^h)p}\9 ]zJJ (<HxZf?209$,lFܞ>,rTt6.|gWI£3M]M&yz595br$(pY?uiah=NhӻZU3Fk*y4ۚЬn/v w: όBg . ( XWgD-F!3]fDǡԃ$ՇKÍ 7J2}811a];]#I-- }xx}裄]r9`Ks!Ls qHb'? ?בr*Xy J~q >}wV[\ 89:b i|1WJE}ɠ=. ȅ:F!6좓S'6cؾKRɻHg_*!ۮC_Swi+jߘAߕ3 gl;4O-3D-l/cco/K:ΔmقddgkB>B,g-Y<* cJ\7<5;Ŝ,5ñst\i7n6VhmweAs?X]/:Јնhrߴbӎ[*Xs{}Xaܲ2(x҇|,JrDLӖۢɶTLY8G EwG94rf\[\Q .LSF?5H@S>N8Y[ p|94ؑwXu`^E{v8~|-1l~'dE[ŋTۢɎR!i–a1Nݎu>V\~|1.-]|f>.le6ڎѰcdprJpcF_޸]}N9PhMQgvwƾ܃ӧinsSpjX$4T: smϻz8ٱ]ƿְHS乼"s9oqXot<τnB Clc6C㰽7Hrxs!̇%COIw%q[]yyg}rV{cu7i=\jY^]ֈ=e03c ԔcB99v 91s&=`$kIhW*#E$yyG$Z9ufD9dI(&ԅa0r8B}8C6,T!5oRBfdNHX ۝}ʐٖBXFs>1p? )/Dn%8eR_ҳuW#BebZ:(?g?g$*l[oMoTQ~>ܯsĮc X--ajh_dγp*Ξq1LRt+oŇcۏ:WL߾uo꣹j )ƮA)9e+$& ;}ZKWv[-C̞ߴ@Yb+q]{gd'o=mDv㜶xNn[6Laù}[gZ0ehx1;6YK߻M{Jǔ:EɃWw8!'?CwC=);tvk_hF>u8SkYbs({[;=#-#Xn_=uU '}CS&s;pn~m890NIx}o[]n?ܽrE"aQۺ|Թ+ =%oˠj0CGENL`ʾv_WpUNŰ ?;̙לsfoa=]@8g_=cZmQdprba[;fumjѮ ׈v.' ^W-.Ԗ#%=} K̚tw.wE2Nnam.'G玘w ^cKcZ}J#G_QaZ[q֖+rhduuvw_cTO]чFv8vщYjӾt]|n])`x*73w({w麿/];mbH)%k8y*t8sˆ ˩N'Qx-GrDY7ez9ϥtltS8Օ;n]={Ȑ'+rjqr4>RJ2K#£YQĦ&KbvbS/ӆFmWl =,9ۭn>n0vFKg1̠佒 !A91(rY[1B-˱0HR*-yg^Yt-V`'5{q=mȳݝ}#7SY‚!nܾ?3o\9{VȎ_?>)wͶ(q2Wc'@}νN$EedY>允 _qڀs:`orr~>S恈mMR` Fq_=DP 1)~m;xa6bcQ?/?4MT)Pj&Z)jk9?yNdN~ޅiޒgl]9.iq<ɳ3yjdCgUnOoum}Mɶ Gf;C j}8Q 1m&W#@A=3Q}"Lݡ&Gc^[C J?U1GP;[Rqްd2gW8_9e7eEz &Igk0՚5nʶ&+'GKB?''ft{,=–Nj+P9ߎ`uVs(xrmcMVsY FE94HiUD1:Q1]܏6W:_9u ^,4}Iye?qީpHo}08ա>~wsgrE_,,_j0dONNSZ4kgPԋIir*q8E+OeyRd㘶pY^/OC97erEg\oqO ǿY_cVfPHGx~5K~A~ Ne$|HЇ$;l99ҡ(4@>~|Kc(kT>]w|g IH el-}SwChJbA}PQy6Pyv>,颀bl sܙ3s7,`v̹{fg=sHX-nY<Q<3ru}PX%cIbϪV@`\3 t+?]~˦9Q>t4sڧ[ uiñ>gX( {}}8Z|BGM&5glwEUL@ɠ7>V6OU3`{Pξ/ywd xޙ;׿϶STqr Rf 4OHj!B&鍓èď5'ǒ@G?$-\(-Eq v,7/g#WVd9PL<}X0~] "(H# rZ]RNbWuZ' |aq+]ˀ>ռebTR!TRw-/KZϜ뾹sdh짘TwwPVnTnD+<* 9:%PVXOnp̅r_% k`R7Ǎ}y"\gu9dcyp2w}Hƣ[jd]߹h<M}qrqBs{f !s$rC4?wf0ZY~%5K#&j Ӈ\;mnW R(d>*BIxTg@ǬgUgAVu|>S9ǟPɏ7r ýZANiKKNq Ns3ԏcprV5\VMԅup:Cs$kމ"!w}ow6}təϝ=kG(pTcPSJS_ E} sG%I޹$$HȳLU9{.uQɘvp5,o0JV'mf ֐Cs < 㢧~dDjc DDž~$Ƕ 'Jq7F7ݭ0*j݅';v7eZ" AQO?n9][PM(Ւ`5Rh@Mͩh,4##]4xȶ5 }PyΫ^1'kڙ.<IjO~+L5UlUkun#N&Y/+:YϘl茓sv$SLBJ uL׵LX֓;tcqrvM{bW keieɅCX&<89%pHHnJZsg-AHї O5ɧed,J["'Qk?hbQ4pSb}TT .#ʹ#:+f<*Ir>6Pr<ώIj%l\ gmhTEpE&@/i˹DC8YKg @/56)dhOd~yc5} y& <rbk^{?L=ģGjd]׹!uΦ8>o@T %ʰ Pmo-);h)vSss)V] yn(2ER\9) w6]y.˗GNFz) sxIKah< cbX@'}[iZ:Ë`p$a86>QΤ զVu}%--vf97 GC2ĭ"~V+ԇ2],ԟ[, Bsc; [0]]EvTN|.+\_2?cloRm k}bjUd[3VPDxD4 7'kE[Zcj<[Zk'7]gXuVbģ+v 7Q!:V'$:e^cSg#UwKq⹳]_(%ͮ.z ƆxWD gGTL ?04S}y zg@E$:|͛Ƞ?}DaO߅$#z H }D?`q25Xr0oX܂]X29n4Xw!{Ju+dH­}$wq$IF-#ni__YZ]bk\aFp'?>_j򒗏lm,9.8O7O8}+9?jC)1ul8Y? :{Lc1qɺ̻s*l&U:6 Kښy]/᠕c{3{'Q4ܭ0">Z؃Tz'#9qߠ^4HN3EWzPԂIJ~^@Αv㋖#W989(җAz []iuwOcI_^p<|!1ˣc1t֒H4Ν 'Ay'KۇDwzg습ﶋէkm/-Gg/g )x\3 [It-w-9bcδWR Wr7+|ha~oj۲pA0ң—xW;_:8,F. 0\?fiho+ r#r xۣЧB :K3Ex9b:+G^KXB /PX43ݸumv`v:pR<7^:x1};'7h̬㫻u%뚱pryWn?OeIGy'CC=cU˻*(E V H0:ථgwp]JH.Rn4V7&4VWx<ώ'槿GӽM.mGy6< 34Jp._=_\qeM@oW\3 X]iO(OYmǢeQ>pNy椪 U7D>֦~drkYHΤ,"rWSA2}wt- |u+doㅛN3Jh}8Y@On|fABv|CX`8lV?kdJB`oRȓ: 3GvID%Še^f8pr9߭_ϭqKTl830wtNЙɾA B}Tpr͠KB9(Oj>4*~lG3;N&^N"ć$JQՁ46$(N늖 ߊ9ȹXl݅Vp6;ܲω v-ҲPd5t4ih_BOzM.>|6<iΌ&(g=lQag8T'J&]8Jϴܚg_t 7VF34sof-UJ~q vA'=4&UUɥï2el**$brU+`ZT٭(%e`ώx‰(axfd) KH"{PLIUQYvw$QS]yҩCsab_tr>%}h99 ͵JHpJ<'"WB}Mk DuXv!<)S+lϐǭcLVM. ˏ,n^Q69eeBXK29jIc.9I!%cE"=ְEuG,2 b׾WnFk@9;wpou#燵mػnrn]6]a,V2ou_@}-j8ٽhS39PdF߼3ur9 {.K*MG"wO(GFݴϸ6`OЧ6kKN{/@34t&dcn㮻_Vdf $<$h,y6[G'p2 hNp$O(7zهThT׽pW@yhGdˮn|%0,(p~`?ό(nxn%b=Y9| S؟E S~.Z̀[MP90MVI;bXNF񂃢\@UIs;dx1K_A4V'LN]jߟ\cNY?<,5iwKΘý_5_?`v DY !)/3<OP 哭>ӣn5 $<<89mRᾖPPmpr j?ɣ{}'+bLJk$V6}8!Ck;^]|Ed}c?YBu)e-%b$]·A4鐬ro!bsj"KG]</l0 С{svng%e .?5'eRM]>N \7>pgj8 є4lP0pSP_9"ks!^c>5}kϼ=wao̼{ZҙDcW\E566OgG #Cr$<: !V \قde}6usumuqM7bX]!>Fs]3DO6{{b{#|5׳=YgNDPۏol N9yh}h͹{Z^G}F藇f*ѹ Ip2'{Www?O|c݋*X8vzS) a~ xh9,UR>̖.wyV쏁dAY'?"9c 7_<$RG 8I$ ٷa~9馷YFi''z` TևR?8Y>>v\'aP+|Y_M<ckq+\ID9@e}]U]Ԯe|,Vgγxx} GCN_Cbr% {8+?^蝋kBaz?O7V\1{7Kȡo)籆/gĤ{_\wPm,]pSʽܳ$2r}b}6+}_rߵ[N*uXw?9w3pɛV\4Ϫ*ζvf1a̼o[o޶h*6zdq5w-:q2F.qX}@ȧi}攕X뫽|<<"ՒH^ _/'7oF3!R!Ϯ,+$ѓwmc ~.vO/'0)6A][%?\dD%`f;]a6r7ʈBD!&80D}; J4tu|4w_UzYG&6NQiP[lɉ)դ;66oЬ Nb[ੜb̸#*cr/DS$hiRqh`Rܷ<$>iQȤr Bq8$NM7'Lr_hJkIf8DͰua"/ǥJǕ]G Qx 'P+z_ѧ:{P~u=Y}IRqW؃XhQ4U ?d-a<3N'[,FIѲD*^O6cZ\_*XZC2W +4>ҨI9j @[5]UJ aRUE;מ&ƢquxOB20$rǢ3E)R>Obdc6(dNȗYN0\D3k bkkM鸜h]vA!S ף:+Ъz:-4?MG~)2K}"̟ykWIQW =$(N&d3W!ωFƜ-O2#URќ3'QybfB789Z`+>q HV'kQXܨEȭqI.BI&IR"Cr,'RDCb:`;v.A”|O6pydP"Nօ "0%"7 #<6ʍvn*z [}!(O [YXzt0S1%\ndbQ9>J$8Z}pPQ)[(\@SIW;5qoە6prXM2(d&NF؃0(\vTӇaFŊcZe' hK7F1&o\ByUb.xhؓPVA1p&'6%'#dyzBLKrVp)IYS> $e1)Um"֠|=mʮ'8'I|VJ2])֘9jzَ6 ' Pڃ|}FΚW˘ LsՁ'e`qـOc;H =W'Q$K2V0I"FY K*"EŘ&DT?bJ@8lR6;o qIRza]ddj89̈́  ڂ9`t-) g6J5m?. 0JeU`rGe&ڲFpralsgj)\ӭ̝ZN- df Jyf%BAeYEi}# &J ,M:٭LŰτ&Տ$nw 8٠N6H|r".̒WfUk3Qxd+(i2k4G \5{ 1ڟ6ܡkUrH0b% rjSEWؓ1~]JJ"@3md l )u`^0dKxo=w0 g9k{ neDO}Nj74K(pAh*+<-wbf{lG'Uĩ7L+o)F䵻$ݛutr%:RgƚmBQ_gd l "^%XwѭZRPX٥g0WIS˫`UeyRJh?a-fd\C lFb !Zq`81.{ 's{qM45p2&vzQZ9[rAJdV6*$n4)M`ʣbIKTBkvv} 2pAN6mdOKoOn9|ٹO?zea 4.lpr}7{!׎Qµ\W<엄3Wg؇_֞/<8ePcx9}D|2/,89zWCKWie}2h?Y9ZA_ru}~kH Ϝno^u3[cWqCKD:Kp'"a,3> \# :AQ$ᱽ]'8EC}p`R(NͲ!x!C>:SP/C.E?ȑ86kǕec'xB^_o|ͺɉznT9zFu`{Or4bU*}t≿ {">۵r蜷9y?{§6ݻc?ݾ(|m>l/8]>lלr8L-#e64X1xs}Ǝ$KXpx>xCZNx݌=m[lb#w)3b-2+)5wHQt}wgvt{} s^xoc ~W|}3g q).9#6Ź`bTWKoUkxNqZEv}2l:l"TecWQXZ7x#c.^Xc3O}=kWoYEhlu>8J^VU78"yNE&'?v@(iwF>Ϧ٘[FUyocdv)ɢ{0'b '+ꣳqhƆҎUz,{zp2HZGrqavf&[/bKi\ r$R`b \۞y yF*EThAe}ػ/BuQݙ"ʾ-W=t#ӑRd)Ď34 m}km|8mh7 #}|V_q]/DR?;o=L|ՃhJC)y?'h}Z {ʺʗE#vwЕ}U%0ZƾKT#nyS~'Wǃy^5:e8o6D`.]>{TG'Bй\\IňtD]CTmGZAef"rp+vAr~3)3/wb}EHGv C؇&rd*3{{u}e89jSޅceӜ!p$:r<]eπ?STb]4XGC]̀A}tW8hi5>|2K9 3P|u}36{*(_=Cw/ٳJP9!)8nЀ]}YU_Ht& aKy8?4ndIWDItxǏب\*f,R?Wx gx};CҨVS'*G8'7}❹{~\;->ٶx` ҹ"{Lв|#8`RC!<GKC5[V52@}Dtb0'CƂ8qȫ G| ~Ø?ŞnGd*ɣok d1}qU_8zsyck'Kclc<Lsmhɹ$6go@mqwcޞ4}M]o˲Fg4Zgl(av!vb}g]xuHggBk nZY "j NU'G&̙ӻUW[ G̔[Ÿy_n[ln;}8׌fpr~l0Ex }x- S#߁>: 's(ѹnǪgVE'MZswn 7%^nՁH~9Ş:,3)pz8 {z0ֱ(aq8sEK+sn0q<816CG3s cy:РK:ʫ5좴1q1ƍeR[ENpDNîoCUZHSU_zY19yuMf(24ϾM8P)[XǟRrbc0i&SCNs3E9 O_X@JU[A''Tcs&skF`euDg"5'ײy ryz_PzQÃ{’|2=!G?퉍Xc]Wv2?#Ɏ/~dD /˫A۷h&dxbfS'-u#=g*{HG46vO*? }MM2A97"LJ <9T% y`Dž(qÜ1곤?l-몳N^~m^{s_3]Ͷysu0{s}u=W~:pAtN:'IsbȭC/ 8%f#xţ^W0UJǒs1ieR[i,DG2I5PSX/``\4.؇4U*$ӟEGF嘰0Xm61uq^M(ڴ]>i"(ԑNr$'|es28@ߟ_tO-sg2UUD{T,+J, x&?XzEsDO{es__E-\|6> bY?zXms|O?#ћwI>>Ԧ .% !Nf99NҚ1K{uO,Xs߹-]80.^7 ^y!Dz3d?옱MAIGIW 's:VޖN!d9}2;Dhw.[ 5u+¡R[[< 7 *l7dv^':~,S"aD&9KCS)I5z]B}v,U@E }oba^k$)D%R}x8&<&yr9s`nCC,r]77z;>tI,TN&̕dv@JkXCHt.>4uFZgzOgX:  bY?r6Ū:;uAq2>4wVxHS[V>t(8.BKy,Kxx,Cb,nC[,ZNjEy}җѢwPwVhoL='VDg7+JC* ~`gү:[8C49r~{j`}/` }Sl+>/Wm<-m*  mUC-# v_ q.G>uㇸ`H`UbղK)O~.ĮRrVtd6,"9]prбD~vנKϼdtCEI0ILv~pw).;ʢ}#:AF1aՍxRi86OUw7AޮNl[in'S=VPzD?K~-9~;x!Dvו@ x/:d*4Eu^@).-Egl{ '.osԇ^<(Nvd |ϼL2#W?;ʅw7<7_[)槿j`R:_)ܲ }GgTchO:✋Uc,

87sKTMej=ѱܺ裗zY79ņ>zveoeB0m\ǥ=d,}xD89=Ef}COCjk]FՇvmظe&Hcge&㡦;,CC:bcˇzty'KC$v_g<%G8ag>\WœG/DN xoًH}Z/3>[f.C"cqx9y-=AGeL ͼs.}藇=v~`d</m,J<`W~5/Ex`K)í̑{)c'N?Y7XmQ">?Zvt2#sVy};ԕ^Q C>iZ\ +0˞}kt.*ñ65蘹X& Jl~vKq2/noV J]<6ǭ+NxH6;48n׈V|5x JjO$1d‡x ?xgI$s GMb&tnin=}<ƿg?7J(TW8Yu4H$8Y3јf^*uR|2{6o, Tc6O0yLNVuJ-w~Kxϫ&oeƊI"ɪ?0EaQxzS-ph NRHӬ0m.1H 8.K-Į4,7Yg,5dPϬV8Ih6CBU!"Uzζ'cS3Ql>pW,g3%OO'[%dAdAdAdAdANO?Fŋ$̑@;-}5`5L Wlu gV _UC`yj2yM,h*[ +fa}`w'V3/0uP[TUOJ/ p/;ؚ^$DH7T^ٻ$7͔USi](4Ş2s2nDŽCtc^*qu T+?l<@wI@+'\>4Y\ -xmmA0%] JkeW+M.J {\!r\g%A>XD+ $HԓC1rxt ).b2WOQPUiIiI)%N|Q@5@HdE,.t[tN)LBPrI $` &,yIa jTI _)MQ}sh k?dX|X7{݈58YR$)<+zJ5CZ#$ےɒW-|1 '#Q6dn0Ƞ NɖY*M2kCp*X,rlkNV$PDhaCSk =J!%m0g)0PQ/@8Y^,[mJE1`lI~*[ :qMT%NNR<%f<=M 臓prju@²T_,Y;h$lDq*ӿqpA 3_J8F 2yge[(-rsǻw5 ؟<|w1HǭMA)pr2. N&,X ''[1d⁓ѧ:>;EԎK5R,YQ5p)svd J'Mp@V'{E⏘-$7Vm%5Q/I;ȕneķ+)uGBظPWAXɍx3D('K+"S7GkV qz`ཡ"{=/|w|<{`?7 fzHqU<)X)n0ȠNFP@ -(Tv#+VSuɄPD ɐ` ?H-MF>cN.$/-d#t7 A݅dN!.mdvO`q2%6l$e>așLcKK:ӁB:I}2H7Bpu(m'+Pifb3V nT0SϔOϗc企)[w|e"n)b{>3ʑ&rJs6o'~g]V'¥k+ub%( aAJE4*v؊d7z*)T֠ϲO&"N۾6Py"`zc i9+_4jNV}4 pkjo>4 bѾp6p-ÐJWis8\}ݷʖTTh]ٿpMWwݏo(ةGnx{ уWM7#owzE]e=̮ܧz^<>\|x fތ2/XYW=oS[伶QMY gUt! z'9Y9_mz呋G4j0+sx#-؅5Zd^Tދgx}Fת+Wgr$k(8gro|uQ-h_[~?W˴ M3~nKqlTԄϏסȬrHUOs=4e[0̾M1%G7|V4~ffε8[BpևwA_ޓd p΢qk >o,sD{^ieN0_'hš!+69 Q"1>v"t^'3T;,vd]q2n.6 #@Q)|J v1 sm9JoVd($x"o''.KEd8ZALlRB}FTͰNo{Zn[Zt`Λlch o_|ٶ9`wu"97٭ɕf=O~xxR9txVC w<uvGbwbG[bKVù giw}/nrU9Zϩ`P0_$37|cÑc1vU 5Ԏ~W iyJ9s5ђCb9IcL?$!yWQaձ9X.K4>c$ײANVƟ2J/D*Nm#_EחZIԴyלjJg[ 9OfDHC^օ:%K"Ncc8 0\ -^Xnd|˟gK,8YC#k 8*UA^<ڥU:y6V}OGdd 2p#fzmM9䖝_$ɚr{>[wx~k$DK'}\dX$^Ӿx?q.cdd 2pʏ;& oK;8@5.!~<ޣ@NuW$'7tI0NT"lF4_z?ij^ošS?.61f 2ȠɎ L JjGo;{:sXڶk!)&ZElj8x@,669)̪# I[5ŽC@ұ 3X#" -`S&gѥĠ+q;Z;,Pl(F =}u=u=MXu*ex+dEzjv,ZpKÔוP0Zr̎ ɛN_?$#zcmD^|Շd,KT{VyoUjmW<`xE}~&+"K '.z椊mVXO覄KnalD#"'5prqOH? 13Bd$!kΫ{VحgSboAdsEs8{~q8ٕ5 ʹT6N2&X]x KU38XmU;@̏-:0>~]ך]B."(V<}@rqr}?R0ЋA'[glק&H8ޯL҄BAWfVZ%9^/̼dt \O%/ǎ8a&e}r3VĜX[3AӊoL*E>0r_pk&0f\a,ͮyEl_7"'8O^naAu(N 컬<%JbwTr2LlMf !,)kssķ=}ix3{qMo޳RD7r-(^->X<ևl,ҋG'5kNJ_ˮ3(\^=gsyWZ{Ec};\IF^?>XeeHrysx^Xqr 2(89j V%`:yrž`IJpA 2RqY9GpJ"6AEXCTNOPTI%cこd :;873hJvr\MxdAN>_qr&H1meI-} #A._U ]4T.Z%'wE|qrB,4AETB,'ڊ LZ}L?/Eݝr/Y\0\ή jdLGu%fvh7fMVgVR!ўN#P7Os=ܡnPBy /tffE gҪLϦdbUwf8jVTvXURw!]+h Q,ق w!P\C 2pWwY*"du QC l#$i[EՃo1u_=݁L*zPl,UYAnuݗ=oܵ`GE_m;H#LW"-Y^I`~3"؝9 |/`fs2"= 扠Ivg:kqNG{\'16W"YWiprRe)\aQɥvE_ s 2p_"hmgͭgrn<.Yj "y0lBB26K ̕ ؃>FrV WC>Q,Ѐ͟h2G@>X,-*S rۗ1Q]U5\^Sv+U'T6j0 2Ƞ |rLl ="b톑@.#AE g߅N3HJ9̨cM.F9-<s 9@R=}L6aA **Pf8Lz orBc `7slZ #Q]w-Ԇ R\!@K @ y{ : FjHF]:I݅-s$yO 1li%5"^19g(H2L:Oc}ώ$ Ғ4`BKuWgqJZp2"mi (HOo3_p9\3k q9Pg@,\uWD{D dpFcq*ȠhŻpT" }ݸ,оb* x[r.&=u54vP&z-@NFpVͨspT4J5''[$8(0Ƞ 93[-a؆9ΎqM3[Rru,psQ(0 AcW+;؍),^KaSPK%ؕFM` , dˣ"8×d!- <1`J0Zd"2TVV Su=@Huy|2R HIt/ɲZxML쫕%je˶ ݩ@&-BY0Cr{/;s&L6VU ?L/3 kprdw0􅣇cѵN ;s{GspcÓ~Ab\ISe*'cFZ9?ƳhJȁ3g?N<_"O3{Fvk'nu=3.˘3NN L  kD?{+֊\=uM i?/8i@~yB3:~j'N!d+8 DBX`S+'6]p2N+5Z N,u 8u|4lH--B:V 8M/ի.)e]As?58F6d_ EBu fjAy!Nfs>]}*鞨)a*Qa @uEԱ2uH`Sh>\f\pμsr~u/z=`c{.oUl{O=>UXb-gzYo_i!gaI8qɪeKZK: '9zФI7^~Ä\Kd[ySo1E;V{ G^w\ݽcRۆ׬;\S'I\0/ny{T-QN_1:A|`].?d6ǽiW59|NVT^`ޗ:)UyX\gIup/=SKaZ1Yl1Fs=;~;s`ukB9?ҢLhЪ;DQr V5B;)u)_y &E jW:>uK!s~wbN!4O%`&AʚXwzF!|KN5YVW? 珥/USC4k)咠W; ۆ|{O~DUYG@Рx婸f!|5;FUw!Ɗ mBY>elu D;N|v}psNvN/))H\{3q?{5X;'Zr_kc?͎ڐ#NN0uڎbo½7/t5:RB_ch$E9tT7 ⟣kFb[yzv=Ɂ\T'(5nK@CݒW.d<؝e ipt! H~Iև_͂@)z9Ϫq u|ARԇh4qrmF>bmd ݽuHw,EWݝ+{u`s> y1˃aPq6(Ef6us8]a<&MA*ZQ|rN%8"mH޺ {7yl8(Nv{<~i|$)xQ~נ ޱ緟ߵ`lAP,zC{,8Z7Ex?qaݭ`Re,;Gv͋R7Ai[{˷n_92Sstns67*?b)%1՗kqt)kWV8m+@.VUثWmAɰ/:=y?^^p^6ņ 9qru0P9I8jي!ilaFEuKmҟΩqrh~NJ܉j n/P.mEZwg`n]>JQ$Go3M\ =𧻓X}g'ɇ1֮/oi/ÓܟE'&rp|gec{X]lmprI GP;xL`X@TYt}m.`Jh<]ZŴ;n/={9͝s=2SJ*} |i)'sjV67(.i!OyEh] }.X_ͻ؜QSn ӟ:FЛ_*XѾՁp2Oߝ}yӕ8'tn;~̧,E[ܼĹS{7d?YhNvvyf7uo{bͨ7ϙ0Lkqk&no"'^|+Zdh X48}Zn/z>$3eL316s?Wc>]]"? H1 wG{԰\={U篯֤bhW )˫ՉM[&X( Y uƠyGܯk5d#4sBy ~u;q+k&Jc6hݗwySɏ|2 >8/AxD.冩qsDx p=ksouco$1iw߇.?^}ڡoC[k]pȳ'os.ܶmޝC{mĵ.)>eݽsE^\=߸xyeka'Gq斛ApЦx78$VjbLI"{, 4_yNfI*{>tϭK_Ro4 !OrZ{PIoUj]$}}7.Vm+KaI(S{Zrrs4fww]K%Yցe}bb[uvOO6_!J#/4VEfwk(=qpcoYAٕBYO`1Q$0p0J6ͰgݿWh~KjV$uYD#D׊03T $E|EQN,9__ pRl֚w㩕lЈoR){u5S|W$i܅]LqN<@9޺x4nI-=di׷v1Fi1 ~H{:ɲ7=Oy;pJ˼lۛɴdylts$)om$&/I+?NRI6|s?=_FmqAU5INV:R|U*%>Ѵ4yZK [SM<]34-\4p}g/;OcN&@1jk%j-0y[̖gw}Х-\L.Ҿ%׃OmR87 6rNvG~yߡ4> 2͸dWL$ˊͮ,C緢 c~ `CVýuh/no/2 ''m`<'s`szĜzMQOK`$eh8+u`"1"[Bj&)^g!޿s'y;{]U҆"3w)D{"L||[G%0oVDxJt u:T]NEV%㤞^{!GHӕE Z2CX4ZFn}/5;zd6T ߤm\Y~ɺ84\rȒvz|ŀ8mh:m{ F' '|eI\E֣p "e9kl$Ɠ+0EvͲ*ʫf_]Odōp6KtaZ4`yϭzumؘ>FUgۅtdZE GժaȓoK$XYuࡗ{M=z&w[D7,S5 Ўcr B< 2͸dTPU4lö*'7j|+=b-g<ڤIp:>*WprĒorL2{="'|]Q*_MB ۠<,:[,4ޭlm(J[ǧj/W8&k"Ec,=|r0]١8]P_@aHIڞo;Dob![GN r/5lbLz]NoiꍛM7n۹qv\hb&Wg ,DgZo'8yu'S tC 3;a[ڂlk씪R 6oYy;V[:{פ# tta Ve{$g(x/WhXtNR+Sy,NfC<[Z=2Ap2`{CO޽hVu_pm;ӫ\?NV^uܚCk~wvˤu<.^2UXs#/檟{dÀl,_ ?prv?ɔqzxiqzFV3l~=C# rՙKpna<6˻`GdSg}'YvWV=nwMϒ69>F/'=l2Ov`a)3?eKAN>}Μ@,2$$.M@jvjK&K Fs}ɝ#Qj(y:KD$63Zܲ88:^Q/''il9I2z$ i,;x?yG StK4z T6=]̖y>> WLT=)օYR/Ji PkNhT1>+9~(gxzE[}^ĢvT| Z$wguY!վ_Q.S'Չbbeq8*bxlВ*&C4]MYEcFUɉ(og@[|HD,'Z))bmH"'LP>FTe]R`rpr$W׭1&a-=$"XU"zL&[`C3m|Mǖ_{z_=b޽{s,/4C>]$rnРQvV}897ApGf1$Oh߃rrFgm9|=89O ~ME%|7trYa|Z혶s2]sjn{OZxNfgS4SE&}P[o~dq'ж8X} ?YCo8U\ztE{Be׽ ܇m=a{p4sU}vPg.|yA'`b<t'|NG-8QB]S*=8OX<yQͻ`؟RoE Z} >k2sLE{ǖYsٝM:.3 i{"^×bO~^kpzm~4Wc+uJ:~vzM-d=oj J.tye.a㱲pmݭ{Xde,_ (E=H1.zmxv=-">7,Ӽ9YMÊղ<0sӾXa4@ x`""" ΥKUjDd$'%TP1NҮ6KP}Q61V^l`w,g$a{\~**;z3vbJ}@@>=ږ}L4?R89 845'w7^b|F[\8N O81$oڡ?{4km W|VGfMZf~2o(1'˺V&9. f{ !{Έ0^19#NqbG%^_To'c2~3..4k?X1眑vKнi\o5?2ɺz|Eݾ58Ǩ"%fWO?%\VVؔ.mƁcdu6VSx@2^jts%r\%wcΩ(WxZ=jtn6?ye'+xM-*Vr5KX0旇R@Wϭ3v|c&$G4qN t ߁_\iRk>ё[Z錧'&N|\f!}[\O{R96>B)},Vd@RaL+Xh7:wH-S!CNdXHaҔBd'z'b)tpuwfvhpMA oWjY#Gq9.ѻfa{].eDR}U:c@.EdH&G߇*Uc$%vl5F8wc %YX4=)raA:Ekx$z=~} fK۠jW쭍()kO=|'+lu1 c[{}Y/Wdyuli-O3VU펿/>Wdߡz!ccyʯvv +H9uϣ霬LKUTU(rtN0@[^w+f!劬/ng,Qoɹբ'~Q"3vBC=;OT2BcC Qqf0>_]k"l헿z:[iW?s|#y.PbT|9^|{j^x~hc1OCI50:n6I[8iJHۍSiL˾o0@ tGP[pK*9.h*W遪b+>b@0˱ﰇ-qYԣ^lכ6BN.r2lMϞ'N{g+ݲx#ߤw+W]Gg7!)sO]W齃 @} S9E޳]?˸vݴɁ߭吪d\dɞӡe5֒YJ}9u".tYDeC#('"8.pmwm yn ĝ2.|d9ֿt8ay牶|cNL( xZ@GdN06.95u|Y~ѷ5¦rr(h<0a'G0x~5t+x'^q\yji>~9r:^8xmF.ς>yZ͜+ݗb#u;b*ٺ& Z֗J_7O'+s_/ 2!s;'C֑$I`?'[c|"Vb}3Y ~suek J!\oq}Myy j84F]J eR9ZD#݋oVW6S.s-њG2LD~kmt*ZޅoVKG 6./1BK]h&yJ}n]<ꋭp0/g=w/ί[~_WN.#M 2RvMp4fnoܽ{zD+[ ˛ PgؼO4_ SzM.0w؎/ݏdV~u CÈ*k_3:]+n0f݇c㕵>tc;?| t Kvrwľ%1Ԅb{}!D%/~רCrEw >!c5} f{MA d}/NAy*3!}.Dh_JL&u8ӛ @Mi9Pl:קh6\-& "87BN_]S,Q-XA-_do1Cr2R<=~qfOޖE`fį\jJ 6:DJFAc/Հa>9,,]FLww<<ĝ1k34p9 uq6{8z,.{wHV⛡dx𫌗Zݜv@/' |ҎUe׿i~Nv[ sv8֒W:+ye$yQk N!1*j>L>xQK̭fEJb]&^<qi[Ζ)QDŽ3(Z{ jhrNVZ_'vLtf[)mN(\`y)MdymzmH ?L%̈>L(C=)C}W>q.q|u9ˑ~LxYsC `.ԑʣAnñv{,ȉp#***?=gN|?Q.őJug4:]>H^􊆵;!0O~>ǢZM=_jmYq2c$'m//8?;x`Ib5>>ľuI896+e)cZE# ?F!tt Np'_mL˴*Xc/O&7Jtb]C ''M^^v-ZR( s4n"X'NVΒʓŠ~9GYRK(>6:sgSRnH>uև'냺ҭ G~nԍݐrķ({9}7 CP6Bp2>3m;ی89c3S=dyJ2Ɯl99z lDr嘜|cC ӛJ0}Ce*:UaI(M,JHPh櫙9W9d u0޾GNvhꭩBZ'{ U8lZ h 3v<ɑqr^ʇpJgɑ`AnlKz O$45Ÿr՘Lʈukz3Nk9r*x_ڞuD+Ȼ8u''1d_z4N׃d : Q/عnH[;vp& 1ip',;&7$mftΈFq̎'b]`29n;}qtYl(str_(=4MůJDUD-^{vADkY]92]_,+*pw"]r  4 Y*ę߇T h dQ͌SDi&cI*~Gsb')S5JYe7Y^ip8RcWz2}q]?w_ϣ]`9!p~8pV'qxB7U Zmx9~8*-wly 횈̒u}2E`*VL4 zTE7WM[EoYB:Z(ҥ)H; 'N%[ wΒv||=?qֿ=G1;e=Q* &CkSR;3L~Aʄ3#(d}ew$X~Ce. '8$i>έC~e!" q2Q?nCo8Qs*)dyGeQe-Um Wp=ۛ"8j,t|驶FY =TYzٯ('}1g=7:+_7y.S?zb>nhFY˦rcoM\6456 aa#SEVv\uv}MLl%tEa<{2dϑwK}iwxSnV[:Vf*F5epektn^!Y<9U~[g״~U*xJTT6d+zHx_7 )_>*WFge Oχ'ݼ.(s`V` uqBE[j]:u?f39ͅpz@YGqBC6yrQmchM+Ʌmプ8!\MAzuv'5 ؼzn-@e6"V d-C='-ֳ}~l=Gճk_v8e6!60 Gb|i 26_,z7{dQ'ZeANS0e*W8jaw_8cg"jS'2/^e|.U& w։Ǧ7Ƿ|W[B< t>zFzU|qOm,ͣf<48oYz*8~OOPܒ>{tYi oY se佲B=c9ʠB#YN>`rr!A8}{8r\{貘艦_Ѱ':e,&Mm]NNh0dURC:sWհ49;ڎ%/B4x Ei"MPqMEЯaUV#R$>UbljXE@fl_N.m9҃|]2_vT|2> Q˧+[ԉBMؔugv勁]ϵ5}/$6?a `mVUԐjm~9/?[]P.EO@1QkFTU|bXJȬd#W#= @c%z}|f(RCWϝ51|orrF=jACZvxvVd^Zf=m#y&DOE|z&2Ч_PeY]1jJGK\`b<>5 ]&RN;?Ai[%D fv񎫱qA"[0(hÀ!9$aVRQ /rɂVd@ev,>TqBecqu6Bcx>"6s̛Aˊ̐~E5$*83VY)sK.~15cZɭ1 ri[m_rX'y9meiw39lŽ*M.PAz3dCÜحlG/I<[$e!̵=2[*`gANֹ:F ('zJ"2QfΔ9j =Vrq@%0BڂxEyHO?k'wR%_9 N\VS燾1|"Iq䭍.rG~fPcŞ(oW3GWmPꢟ#bd]bH;4iv2z"{[>0~ EpiOʢPO~΅2}bKAzݖ$NLߗc#TN?EdcVQ99 a\qG?e=(b}0#eȱ~yn r܊4U$'tw@qCLb~d+J8n,) ~|[OFo]eYRxxetcȕ%-w񽜜QLdQDZ73h19d d`ӤI3y V?m^"/jmi2ja.C)+(2ahO|LbQi /(xxyngsPe"ä(Dɩp06V!=[ y/ܯZc(艖LT >PdҞ8 8OicAMJ#R'̭ݕ&U]AY22>^3]n1, 2DjJ 3"!Oԋ|V02yfwn=`p%'aSˌpY9YF2->Wd@q"p6 Yoz+r2@:APJNw08$Rd`gJU%,h/=p4Y0xrqȂ.i6k6qpn=Xqtg.d̞ H . OKd|5z(yeP~rW)~_,.l]]q*G&j6г |Qeq ɕ8YcYs6 1I\uw:7Dm*me:98'k l]zfvwA?)KmdžԠyd[A125y2{ 1ld́dYÃuzYPv1/ h{Sl6wFl +8YfWݵmfsNvvD2[^0*$Lͮ E'R'9`VJ4hmwk vN-ҳTނYɕR_@cuȰ]HM2ռd W&d5vM{]e[`W&)/h7(xBvW10(翎om{R<"AexTOuAq6{,q͍/*~c89ͅdVt,0@%ȓ65ie/òVږF7JL*T,Í؃6+kClOwNѪڣvZoJij|eh'v!%aCIh33Q)Kg<*D,̇f1~OWT(pMrU>HO2 Wwq^!CAYhDɉ.2>'(zgɞ^.eӪd]۲%P`&zd4=,5ɲ9O~YI`y~BR0zTli/18l\:Q{13N~":H ,(hW̥&1|s9dʄ<0>ԲmgD/KU{ar0w|dð=3A`yؿ :x ZDCkprN1ă;L-h//Wͩx|#uQ8 u@oB 둌dj𤴨WtKs8\3 .;#2PFGHɂ8ӠQP9|ï9QYa]>igW_BTvty:ͯ 1$R"b5GD! 빠^f7&+&^?'chNC3'QYpљUf&xº6k[ ]PusAP28YfO~QkL/=K&b7OGD1$ژwO]P֍y/o}]D1aOb8L;[bnl*7('1i)|gȲ\i?{۪wb;4u~vW7LH𶟞~~9iDyebP# <>IL:!aL^Eeb Sa*FL>n [22Ɯ#CK|| jb!C=8QC%jE`^O G{p2='+ONƇUcGsp2 1n0d~t젽Q<)+{ c]ڲuL68s$kql/ ђ 'JeyLJ'/ N{dYIѰ6B`@?>~}n]E.8yx]dp2(R?{Ҟ{C Z [m6= O[)7]G@]+'s`ql]6un߻~u0^[JpkeϨ佌9>MKCZLg"ǯ,ʽӳ_#zQwa׃a>]g28O>{z0NVFf^"6[S]xjcжJ =׃dp]~x~۠_dh̤0dE=`>/vKʮ \Q@CR&e1-{prx=XyOc/iMdSQFIA8z̜m2ds:=,r\P(p[L; C?Α*qTNfr(T&L|^7(YrY PBOj]lOh\KOqw-}'h{B Uh ֨Sbv3'hYv3|a׃ h_jgT@ބg=ۻuaǕR&aY993Y]i6VAYȣTUF}Fm~A^/M) }ǟ REK 'uH4< lSpyCT ĮzXt=H`KudjO,ߥy4=\U=lj=,FP9;Y4ӨVw\V vřm}8Y$N6xtϬq;TOEcN:~xxȈ nݳ%O60'{e~$Zmd|Y\O+_ZCɌuτq$NW ó?hd5_WEqؿLemn=Xq(>/W='KW,hk*bף~8<[N[ ~j'{L>Mfi)YћI.Ëڜ&wdbSuPS2m3ëD2;J(~m`SSNvA@[IULY,z_v\2|-~($ /mԷ}&p1cw7ʜ_tCY2{lVF+|⃚Ž/c7>k0?+{5lPLLoXe(F h[&됍o>1nBlm[Ekcw {JU{UɱNDS{2EO~~~R9ɜLcy0& ʲGI€ժx |WF3I*)0Kâ^3ؽD7r!zoXg1c'`e}t=)t\NcCC z6zjvOe8UoqֈێfVq) '+TpgL@~agה~B!/ӌ~mWj-s=g?%9rxat`η\qq 0Ƀx%LW=AcXϡ.{mhBhC~e䋓nK"ݯ K-jng'NZRyRb.$c0$aX?i)e{}8Q%}|ܳ]2e) >g@*TYh[5.ޜ&L%m,m#h4TCh)p:>#zOu|/''kƊ!9_P|֎Y=6NR]XʺY+Se2 )Du)X(4y#S ژ 8Ӝz|)qR)u246Y;o|O=~jT*P}a+gϏ.C9U]`W/KHe]RwU.:+nxf![5h)4cV)˗ݺQY3j2z{6_܂ߦ zV6j%`OВF Ce-pCNKZX۞8X__ugPAp 6ep"tzŷ<PZj%U)567Z/W"td&4]-tjQ}1-cǠ{Y\IdY8~^p G:UaI(wB9/E3mv?Ah?r8:EBϷ>&B \Ϸ6Px8.th5Bi8'ì!]J=gęçr@j%|.#e aH:G:} 墭-pȁ }dАP 0~6}b뭩BNH {Hߒe0Bίꋒ ̮"L*KikRZ+MIدEZgG<_"uz$P~zWB'yB1Z`I"codK Su8'6u`&l%@H4NAՂoX 1hf0vuV [A]|-[`x+TBeB,Uiʊ;`9NWCiw`Ͻ,Q,492,H˝l-<;7`LX2Jk .rNfQ 9'kBWm9Ry Ɯe* 4-U\ y9ax=ۘ 8zOH8YuHX /@Od':\=O*3H!,x`,ȓȝiؒdP;| VY!rbI0N. Uˑ+X]G/)W ְCX[ BIZ,3bXTN LdyVh,Fi+u߿.[l܊;Y*ͭ%[^F4VXbyD`E`!Ėc!7 $ N,JDJk2Od$9xrϽUbo\ x=թ9ˢ%kr9 P->}Vjgq홁(`O 1Uy9;rru.[)[m':#hnCp6-TGX_-ɤȦ*rf\׸ʶ*jiCB(ڶ&_r.\%hȴ!'?pNp2$Xh14G4x:Ʉ;yB=mI\ jbݟl <9S7Ɍ݅]@Q99 ]aNe ȡO( ŬҶKj?i$<cr6d\8P˺[c]jp_[ BhM6[8M!9lU6貮ndj|{.¹mĤ >66'7moRѮ$n܎ c]vlQ0VG1 '7 !c#|suZ)7]-lzՐgپOPpNF.)/%*ui[w`iL!'?4Nd`j/ҞdH VDcB83=#Jh~K 7/jbGtp?9ꅼM)IJ%fjE)Xhڔmf0X[''prC!rv.lKޚ&ښKB)90Eۆvyy,'=+(Wd/CaU,t/xprC#U4 z(p.쌮O~3¥;RQZԭ fَ˶$4tLhLuAyh?Q5|!#ttI@|/hn3\.-S>#k',)=jhMyЏuSR}a<(2fz,eE,N('G,JA S89ɮX(ocP$Ji22{p2>1$e;MuX8%B8MmvYgdtaʮ=K]'/ݝ{,'cU*r˂gyvR'B/'cVWs4̬&|+a_ɡ>9'#N.>[ ea2j=K =] [lͥ?1pg&'0''<8keaȷAZڧ3;YQ*6LX_W.,-m$l_y*YY<'C3< G~%mT,׶z>%]%s+4`a` ]h(+%uz ]yf}r.0vrq?b9rP(07yt䂾۪d}趱dZ' [O~q|z(w(ﮃiٞ]θTzN^R>`'Oo={ K[hT?ɟ[c$D{bKmxܾz?糭1|ڎ/؂qyˇ])#ͫc}Qq)o~VnV=:#K[{[֭9q ȷɥ8yyƧ"Jvir]ac)8>ueayL,:њ7X rUM&\|OnPW ǼO_zRP܀/1xs@IU4Y>2,u0^Tq2J-2䐓 '%.8!PsLJ$E< R["(eUlDVN+/@?i+(-IчɸD S€ A?K^Vk/ˆ8yRRKAt/x=7 ds~jsQW~{@]ta~4[RIB.mk 2l!'. , Z%we.Vºqy$ЧD`he > Ngƹ:e-QpDk$.&ddo!}wO=VpEx1-䐓\8L3dso'Wuai Xwad}-ϲm&BA%ۋĤ1%! ~2SXN^%]R;O5< 0m-tbFB=BhB/sp3^D/ּEcDsRKpG{T'w/6t]2lQt:u&+߆סɱ_KUt͎ [%݅oXg'^Qz}Ap2Ft+0ڻ<"]-Gk,Z^]4Z@~Qf"nϻ1D{Z99*:dSf^gAU;xT:]g>,rt)9vjg3ef`!6nQS)TJ4m"Ϲ(H*ϕMUs]wɑclyR< ]d!W`6-NN৾m-l!'?y\m>)&jsŚO7\|q>|ONN'Fl1pbʻݴ$ɼ<:'ĕM@}8'뺋|^Ԟeg-gYwAѺnԓ+r9Oh*-s}G<^0\DSiY 'ޓ'ei$ whP}d'm$9.t i<)@p!\G=Nt'ujX$qn\BZ#^]ޗa68y =;/N>ak,~ϟǰq没A*'sY*53i(jAxrrs9 .lft9c8`^z뺳]ַYQc[܌'XRc}$op 3LJH ln_Έ> RhAsѐiCN~Pyj9VTxC" z.k&N^:=xoOwNZ|n]ۀFhpۜ,{ w9?&*d,`, v9ɥɾ:8v7,߅9f=vԛMEKd|=4s"KⱒWZ(x)qgKɞ1W@s}1ѓOMS5A]]15P1x=NFĈ6`.tBz sr/wfW\cZ}Yn %B-s.<*zRjVpF;ú<2ȤڦM3)՗3#%2~[ȰY]]^8&ۯÌ ncoNn+LF 8T4^^o^,J%l;|M|Ո:UfʗY=MMY{Z'#AȽ߷u򏯾^gwr/}WvUc|čM :Sϋ 0'KJ"ndK2ZG.=.a}[x8YH|K"ֈzdEK8AlЗ!e|`38F;d@9n.hSbu(sTFDwB?,﷕{`V1'ϓ46N&NiH@?gsv%wpjdRsp:fA}J?]|'{"% 9arrYgKNڳB *_n/`id`jҋ_};:پ':r|?5?|/h 骬}q|c++W.'GuNDc墼%C128f1ΓK$A6w![۲F:>'lblXp%Bf5%B8[4f(\Ɍ-VɛH485 r eMoݝ{,'cU*rKgyvR'Bx[1]\G 59O#NZ/Ix~>ŋ0/gfdIMӦnarQyUF?D3 '%V%5$j޺lkR? S0{(j6IF! `4X0XH3ZYA8xR79 NN#0]Pd׊뾤ßLXʺXEdOC`Cu%u۳Ijj 3yn^!F9 1f`m]NŞg]'.L⥻|wٚ1zΫ=7 7 BN~L^ +?}~Q[ᵃ CNI䈫39YS3=n׋Gq`#Po@NsUC@BR@N'ikq2-y-7]Z%l'8t$+=H4Vs8fL/'_+MY׼851,f/l 4ؼ. V=)îv9B,#gl`BDaz8ջZbA¹\VD8&e0n>B~:iӼ@"d}+Ib6m]@"_H޺uYV ̌}r?!hv86 @ep{h,2dKyμpJYt/U'/> UT#i͕4' cXG֝:͸<;8Y h]ڎwvtԜc\!) ڱf 9O]8HzZllMU DoxjƁ[hcF8z;>A8M endstream endobj 35 0 obj << /Type /XObject /Subtype /Image /Width 952 /Height 205 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 403 /Filter /FlateDecode >> stream x! & sB" endstream endobj 38 0 obj << /Length 2150 /Filter /FlateDecode >> stream xڭYo6eRIz À4qZoqN+ fb!G(YёG~eֹ#?N;#s}7 Zf fG_-6gpD':f:, :YuUڮ'-Sז$ry[tjީ4[w0𹕔&)"Hnf}ѳPF^9[]x^6qz,Yqj-"dKn-+k׬=$w:N^4 nT !P}AULYHgzF=*B^(,3Gф=zRC4w#dKyMdUJG+M`P[tU*'* , k̷=XڜXg> TI̎mRUtV 2& 7䛢dl_pPv8Z%&t A9IrLf 2z2%dH" zPN#HXsT7?c"xS#LrC&!쒁U,~ΒIczME z7JכJ5j!.|+`뭊TKTʢ!)4U -6[,2)CSJ?zC"H9:+̀r7 ;z#a]XtF dpA)% ' H FA"j%<-x]H_f&N8A(_K] ocjm9"j? -}a:Rxf(VZE+R9>[/47 YI| \=$Ye֚VA@0)Q ZJr~2t~3I@ޮWnZƋV:acA9$(]PM"mALiթC\h08(@t(:EQ+!!3p8S&+&O~oQ{ n {> NxE+U͟ `[x3 ]QB\Dnd_^wbDgA +>ܙEܵ=\7y_ER;4ȟbW&U$e\͸ۗӲP`ԡa{pE7h6Sn'EA?8-hXsk]޷j3{**VI4 ɗ$\jx}ד'^x] OWq ͐k[2@2Yd諊JNiNX_ךʉ{qOoϛ">Dh&dп;3t[!/G%hvGǫO˫Yˈ^'fzFd<W}rdtD2v@ h FkzhDLNUxܛ . 7|] q+J9tüt:'Ypo%+f:-ir3-X\7-c,L:n}'П8@pQ beuCK {Q*wHSfs2) ޣׁ endstream endobj 30 0 obj << /Type /XObject /Subtype /Image /Width 1169 /Height 268 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 39 0 R /Length 82586 /Filter /FlateDecode >> stream x \Mϐ_S?~|?qd{޻79{;wHLNFJJȖ /KB8̗BBBBBB=AJJ !))!gʂ J;k`ڹIơwT.vﮟ *WVڭQ.~m=[AmjW7T][uk3{v56+j xJp{EޱϹs6O\&F lmVBIMq~Zin[u{hZ}+R$C*_^!Y`AlPXJ>кO?[~G|+9ro/۬ԨʥKg"jPĸkWϊRtiүYlmi_6`ۡQ#ţ+mT*UJȩU\np0(Lz5oλj\0IqjؤZ5%!KӦJ\lLkpRKUJYT$+Afاk']pa%sgf.E VC?Tx =Ii?bl Ϣ`1уm{bJ7(Ϛ5Y Cy_ʙS< Nz,/ߘ3P)lV:ᠪ46V"g&JAE.tkJrG>nͪT1oP,.Xbf!Qm699 [LLÇ߾}9ߧO>}޽{~ĉ?=zG.AAAh|bjh3zٲ+VpqٻxrkM;FCsݺu랂ԭ[E6g"TbB~d*GSS3S,X6XILt4ȑyeuuΜ9[h!ZR_^yb5=fֈcԩ؟7ΜmeQsU5b3!vv'ycΜ_z[RJVangY=OAM n`cMu<9^bOmVdy…;w,N>E ɶfiɏy\%K˪WKwٚ6m*?e0i߬w,60J,TOIf3; \QrWL2=N:ym׮XV-Xdyi&j[^d,5ؘ5,)PQ)ښ~Hgg44HoZ2d(ڵq'6mt6,\^:z47IvB1c(8Sa, .7q9YwSDKesP3ZIP7ؾta- f̬|yGiRob<gEBb#{vUס\" B?!U~p9eVFh(AxfsPQFTt Ϩk QfU7)Rbu#,l_>|,4ڵk޽{߿ǟNF1l0|mVhQoo uջwoSSSւ݁{(rɤzW\Aʧ\ߟekk:֭[RCd;8p$p ._:SDgC\]]K+B]d^"Eg;ٳZ ջ#GR PBoCWZ!zu:u=X^UȆLu%Y;z)S 'r!ĄZP'D;ָq'OCBCC ȊqggTG؝W.,_zdۿxݛ'OGb6jԨR\9L71X)E5kl9}tٲegd;jT6,ȑ# rIjl\@",XP27o|"m"olW>w2&KlllC6ԉO=hA;&;4~($d#%ۃn+N:U8-_]vf!!!l{777ŋA^W^ݴi8ѣ 5^PaGdll"]vȆLBaAܹm۶={hlH۴Ag:oذի`ȶ~r133;|`ƍ#;[5Je˖Əhgggiiɐ 'OSJ u)3C:T!AHJl7ƎMW|srD|:{lF|\xaҥKq޽6lჷe˖,YPO2d=z2;wN hPLV)]<Ӿ-[ؘ̹#o\  dl-жYZUljZhQ9rww2d -K*!Ȟ7N 0U6VvsdlHλO^ƠN@͌EDϟ7.߲e Dw֯U+T˖ fdCv*JBq=).m]"ȯ 2eʘ!z͐ϏU!ۿSZ366pG!QBCdblĜLdU( &ByI}}"sp޼yp t[D=֨ؑDdC!otnܘz*x&d#d#dY3ߢxd a!)l+W^۷F [ܹV{n-ZP-]\\ ^xPGaӳgO+++pRL>]vJ??~<*ۣXB dC݋\vܩQ~4D6MXl="[{ =z-A{jɓ d$ɪw{BlQd f޽>vXEdC-=F6=z ߿χbŊʕ+ep޹P~dq|d˙SYXD Ed lYيOlllF6dcAG3ݭGPbEflXFٖ.]ZJ.&L󷧧6{w9 #с~l9dϟ]v& qglǜ. GĈ 6i$A9 Ё{R@6>S-[6sȆ; +D6_D&p,9(EUVq`udΐm?_HB6B6B6]#[SΝLˑv 46%cQ&d#յ$!Usdc?%A֭3g'8b2l۶mCuSBGGGl : e:3s #)5G6I4hX.*UTʐ 6 '7l0ȶĄ2m[5Fb72%z^lƌz A6CCC+G6#l![mmmnj |+`Ŋ3l?gps?Px,"տ4d`#3dLFFeq!.Mrdi O|F8)g"ȑ#=<禚ڹI_A[ݻvn"˃Ð ĩttt,^*d;y$xԨQ۷ֺn]q1"sԻ7όAܹ֯ .({Oӷl*4\B eiۄ^dG",+l?APUVg뇅nҤI||<_Ŕ-[ J5cccQ[ˑ#G@%/_>f͛h (QD)%JֽY l1 ͘1"-QTmU%\+gN5իW`,(CBBShl.S`!FywZJLͅbAK+k/??xHd&V)]zo[B ޽{٬ll>}Dj At b?|/ߟ#&3gD;mmmGJ^8**t W*Q"35!g"=K9f]Ed+PdnذrDO`AߊjNr[:Q!!!@6jGv,IٳȖB+L&ϟ?Gsݺu*>~vѣG2D6.<<|̘1Dٳ0'>} /^dxyy 04D?_|.˗2D[~T\Ysd sp4s&JQ!Ei @9rԭ[WldPW G)V{ /CYQ>:x,X=GEJF믿p68JC-͚5c6+E6Fŋ+`I}}VfM-k@׮];wn@h'-IUTIܸArU`,X5aBL=xVJ#?ef^3dSUV 9$ 3*KӇ tl8<\ mW>!Nj5͛??C9883ܒ#lG"#n3|WU\9"z ԋ"p-/uI )5 Tm dSL˦~&Wl۽Ms%d#d#d5iRRBDl]LAbWuS,16lrܸWCiH jsJߖ-mZ3P/gN56+aEp(,jEGuJz'{ DPhn@e 0YM~G>aiLɧ3 Rl?fnh%\9s*۬=e*Ju vb3f gEiTRG)3b9ߦIjUJ^?cfP: ۷ԯTIiUDT%'ûꢌ`NSQ3AW|KB)RR?QF&5gV-*x_V|&ի+n`"{)Դ:jlIzub<$16֯>mSL!CĮ&mռ9kA`67~(Ӥfx+iX+LL9L.Gf.Oszuƞ rʮ*D06:6jO~~ WzlgM5Q#5mVyCbqa_H2AT}LFJJȖUȆP|XϹs+Bٲer dϖM,no6(?%', hDڬy!̑#핏NxRWG6B3RT(6IҟgxR$az§8#:T {n͊G3.P$YQT8J^ԥg9 db搙lK1J3ʥ\%?s ]-gUgDU0ϹNiS51G':YW\U\p5+ӔMa⤠Qyg$tU3cCi2IR,tW3 l22⚢&A5~f. ڜ;K.xYyKa*B[zuIi-ٳn$у#[lI<{B6RRB6oX,-9}9ABBBBB} !))! ! !))! ! !))!w.G}|$$$$$$ll$$$$$$$$$l߄,XÇ=HHHHHHHHHt-iii  T+2dȪUHHHHHHHHHt-֭344 T+})&NGBBBBBBBBB3366zUतַoIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII}~ (?Lold,jpȐ!ϟ?H}&vJMx/7o~|F]=xǴ4-vyZlmէ}.O~ܿ|@n.Odf Ks= i.u&4 y\!N"N Ak.MWsB~@"4$723;%!ÇZlǸ8Hb`%{W^_ ;\9Us]pӡ@p31V4B74u@zN7@2Qo|)F @m_-ʹ$lllllllllll z_hѯ<̚5臐A6G5B6B6B6B6B6B6B6B6B6B6B6B6B6mM)!!!!!!g"[BBjMB6BGsΝ9sMȖiҵ}Ȗ&&-6v~illF#F 4(sȆ0 KmjE;B$d#d#d#d#d#!d#d#d#drd8p`"ۡCuԦP پYd޽{DDzd[fM8998M6dժU1 >O?zSSecc3}2133[tiddzdC*^ٳgzꘘu֕*U… ,ܸqGK.=ydXXX޼ywڅH7Swupp`Q|}޼yScF!}ٳ6mTI5 W.;B%6޼y]ٹlٲٳgK<!!!lvv}}"?1(;5k6| ѣGu9t {={6cl]{@F5#۽{X.Zh͹s_>G^~;wUǃ}׮]ۉl%]bEǎ >پwA?;vK&~-\oXV.XjވEqkϞ= mʕ|ӧv֋2K[d[lYϞ=\d˞={ɒ%mllX3001ÂkF'577G`#J(Q)khhhaa;P˗/ʗ/tԩqqq2}r{{{SSSNNN>|طo}uVxu\vؑ([AvnҤI+a%K,Xx3gE*'d#d#d! sLPڞ:u*Cd˛7o~O~.\x޼yIH=IK.&jeaL4cM59R]M Le(!CLƌb?\0*Uhl߱8qLOO![RMMgm6%%gϞƍ;b\v-V҅ ZZZ&؃x,4XP5 ڕG.@B B'O4j2yŋ$dB6B6B Na=epU5Ȗ+W֭[[YYEDDl6WUlM6V:w٩SOͥG;E]uپ}Ϯ]wг{w-v(k;$g$ dtŋj-)))88X|bbŊ, پoA̓=+8 O<ߛ;rEOOPȖ#G\`]_hfa^zwM䝊/D5ٳ4 RѲeX?{;v]*@Fj(dɅC 9r$عs̙5d}vڵW\狃C}3t{H2 166vvvVl+V0aB\\]] .!a! $""/% ~g>_PDFF-illlll߷=zKҥKϏ}{nKKKjׯ_{{{FKgΜ)VdKJJZz x?ډC={Bf*ȡC#۩SPܲG޸qlcmmϥK!l_9A,b>|M Z>`WW߻]:gtپosN׮]kɤN:#LԒK>}؝nAAAʕ+[׎hb߾}ɉ*$d#d#d?@iK:ll_35VC:llllIUB/#޽{A\ަ7o>|/^HMM~b;%%%>>>IX?^|y*)T#[gPNGB6B6B6B6B6B6B6B6B6B6B6B6B6B6B6BΞ=ۯ_ӧOW\IFFFFFF#[JJx)!&ݻ4B6mѴ˗={#[&͛j㌐Ml߿OLL|tAM@Tb /l,|Q d&]qV!dC͖OFF=!۪UfϞMȦ͛lٳ۶wo !1bcMlQQQ[>~8!Ȇ Ȉ=]M+d ܹ 4t6`V_B6 sh6qnݺil֭+]4VgB6%""P_6lBȦ@-[VV-JB6 ٲeSu !Az,YBȦ-m۶`ǎ#d\*UvJB6 ={}ۋ/>f ={#o˄ ۭ[偲Zl)))/_滼H/_10qBo}Sa)4)))::Bh8s`gIY ;C\>pȦullW&>Q>|+3CWI!)|DW&dlo޼q.&HG!ƾׄt^zuYr] 1/]$ѣG(׮=)?~f߾3G4~& ɓgҗ,E 9$LPЙsν ߅^=r&63\g\(E$$a3s͏^^x)&.@677_:uz ͭ.~D9mذhlӦMc,3gDl0cǎh_m̙ ,`R׮].'OGِ< ͑ iӦ8d{B+88BlG1/^Sdsss@Nj جW/&uS ccjڵJ4SdCՇ`6`el-%~ s6+fkk,"T پNAy9B.^^^ղeˢ9s kܱccrr2Vϟ?>X&Os 0~qEEEe!b7z~"wvv滈_;NiӦUYiѣ͛7O>oD |bx}m^mq&ݻ̙c?3e-{G7oCBv*dSsYFBFBonzd;{ȑ#P3smffx z~$~pL2NM:l͛=ϝ{ȑ fda!JLLƳg懰u8844J۷oG"Aa cgܹs_b NtppVXaaah.ŋfSar7x*WMf m7N2SիX6ފw1 vr^芥hn3sΌ3'MjcAAf`?'e9٭0a&7zHU?}bf-`t"bt;ٖ.]innv/ QQq'O]t)L~zI<#UsѫqndAY dI)p?,\q.^N8.Xӻt颧]DW"A)Sقx"f 5q/9~*Yl>f*{f3E:,T^C@ވ?#., nnpy@@hB{$.֕%{hg 9ˑ 4i>5?'ͬpz!Jjժ! eYjU?5C@'T~~~Zԩ. vnN7m`TW%aEmm۶lWT5(([HHˆڢE \TebdDD49 szzz K x-8S";rMD6NYU߸0"mbھ=Pm۶mܹoڴyٳ @] 16aOR!Ç (^u$@*U8 ٰj11/߿I %v8€-+omԨqp S)bȐ!SH &apawfe!1B"z%1x۶zȆۺukYp-99cc_MA6ɝ&YL5 @WlUT:u={x"-Z xРA"Ikd(M,Lꑭwi~~2D61 >(Cd ~q?pP###ٔYȆ`ll<}KǏ#!+y&6ibԹjԨ! ?K ـ b!ȑ#+5H2X%?v*W9Dl,݉C{&:B66#""CϨ%9Ȯ7 ZC19JD'OD ǐMV>\barX u`c?PbP@ 6"-[n3gyxٰ!#G6mʐ_a~ӣ*dC!n{(EdC<;88̑{8k-[3fllYG@Ȧh33?8>{ 2lNs8w'N|  M^Һu[٪UhѢ۷oñC61za'ص뀗0Y)2i =S.I %Ɏqi!7n N NblUV_H_xk֬ux7X<(A6M*""agDdc~F پ6ҧOL(ܹs'ȦaΧ{ٲِ۷o2>P3++AK9v]pN͐QE|ԼyE>QF݋DG'B  ڵkj'n#Q[ӏm($ƙȖ #BE E>G Œiyƍ[q C"`kkێ{ܒ، Ν;e̕ndG7n3tl(auxx<egg$ ӣG%`3X6c1!POn ^lg޼y>x0 yx x2eʔ=r8DL@ AیpBP!Ÿ D1nKvS^#0;z(7ƎMf~{Wh:@XK0gF BkH C P`ٷ{}ܸH\Hh D?#` 칦Yl旓G6 V } Y2N*R.erdywtt@NHH`I)qyN?ߜ9sɓqf Y@EAl\F HYH\+VkD?#p?g94a0 EDDǿQl𳓓w裂pyfF0CᷢEΔѣ^6ȆyP DG-ܱʮ@;, BWSB/)'O PG.> Boķo (y6!g?'F)###ggc.Ê[Zx4::z{)WG<:j={\Id_ooo7ؼ~%ׯ^64׮6lRr卯?%/=}4ɽkxOFuXk%~fefJ{>pU:cdCV?߽{7kg!uB~S11~DjBنXϬFu]BB$ec6ٳG1(QQ1:}/*=WD؋oB1&gWWO{{OwwO~[1)f9rDgO''OT}Hk׮>}$E˳AHZS̜#yW[Vںah.e;uD5Dh3{/[h!8+LJlp/HϨ9xeC^g15ol0C@he;qR>!,H:0 廈/@X#pa#u;;;>/ "0jB?v񑌺ڵkHϸGYXX+Y/_Ɏ)`SAxkI7n)~FBbQg]ˆl2^6{{ٳgKl(GgV8q![lB.3wV7MM|Raez C͙xMlb8H=_">EaaСC{s>""xm6Vg#R ٴ-`9?Y##+WH^!Dl6mR* d23>~D0d9U@٦M[udXoHx#{g!)lׯ@Dd5jxz#♐GC6GGȦyS.@6OrdS,D6DH&9Ȧ|&i(l$KM|>B6Ȧf Yl,D6߃ā2;dW7]#[ mulH&/P8}6K!!d#d#d#d#d#d#d#d#d#d#d#dFBFFFFFFFFFFF5_Яlllllllllllllt/ !!!!!!!!!!!!!!!!!!wl'??,G0j}%VV( H+dKII9{9s2ϟɡC?[Y:z٧Y,"ŋׯPĠc.\K`Sϝ;wL.OdSٌ?>},.l$9sfM4@_29}jzܹs_/hp;wO~'&&tڵN:۶mc߿_fmmݧѣ*O:e``]ʕ+8Zjcƌĭ[}|ۑ#Gcw5n84nŋllllll$lllll_قܼ_r{6{MbuˋebbҳgL [TT-[8Ln0`q/~r:-_VN=`hԩ;vLS+6lРqŊbcoeϞ=r ۀǐvχ%k,X022[M&` 鉐MG6| +Wʕ+^)Y$XEgΐ پ}+I͛7oޜYUhq`&&NCd;w.'lTVLV ȕ}\]] Htl^Z~ e޼y...O< ZhkܶmohhL e˖mԨQ|/[=LMMj*Ξ=g9f-H1‚r…gϢyߵklY%j-&&YZZ153pB6B6B6B6B6B6B6B6B6B6B6] 0ZK\GEEn66'N 8xCرgţRA6l>]p!lѢe&-gFFF|PY Çǟ&Mv-u9JmWjӦ} {ى~;qD;s/^twwн{+WHAFFFFFFBFFFFȖrIK`YcBB^|;l?2rMK8"|*)&&wy/+yӧJmNQǏƾMI Ld?߿y&--M2HFFFFȖ:sJFFFFF#aݻwzɚRdDhuF@P]k.QjG:!!!!luBV޼y'ʕlllll#BFFFF"W*l_ՒKhllllllllllll$:;wZXX,ŋgΜy)Yeq\3@yo߶D Ht!}σʒmĉҥKO|ޅeС 5lٳgoٲeBB7nXtg:B#wOȖ3gKR  <<=ͭxIB6B6B6B6B6B6B6B6B6B6B6kB8пN.*Uֽ{wB6B6B6B6B6B6B6B6B6B6B6B6ٳ~K.Q?߿fhhȑPBݺux">]f ZB6B6B6B6B6B6B6B6B6B6B6B6lڴ\:w|ĉ/^|ggϞh0`Ed355mР>?~|ll[>sHNbbbe˖$:m64zyy8*>>K+lllllllllll%ʜ}I&TnAFFFFFFFFFFȦ^X_fN پIKK& !˯S ~!d#d#d#d#d#d#d#d#d#d#d#d#!d#d#d#d#d#d#d#d#d#d#d#d#d#!d#d#d#d#d#d#d#d#d#d#dam6BCFFFFFFFFFFFŐ~ d^gR/I~qG]οqf7W5ҳW>vV]b ]K4^3'^b b=@=kqIb I1 IZ ٽkwߜ~w^\)T!1-`8 $. Nb hZ8Z ABi1`vIxmɨU=HZHHEDz@7>N7/47> $SJM6M>]BIDFFFFFFFFFFFeM{پ?dz~>$Zq>2v2$4ewlo#\Ƹ/l8M?[hȓw|2Cd V|?G!Bm}Ukobr'N2ȆuԡtL^]"ۖ[*׭XY흗v~d}uw?pBN-Ux޶O[<ŐlEĆ"1}_ ٖZτlllllllllyٖ/_y9#뷪4(ئ2z!IS-.]f/^4j캰u;/d0Rl^g2]fΙYwϐ{+?߰иhˢal&9:]42>-yC,Yw";MYćAɃw]%=z;k’ Ԣaz2D6maKnZǟ,W[=6Y3G=_;f 63dOk4H0]nʏ,rE5.ZhqGf{ᦓGf b\N:naC̆= .`Q'=dۋȦh3H3[gֈ`#d#d#d#dU攐]Ͱ@ t#fLDy6j|re RN﻽O|۶fnoqU9AۉV ÑW w_ /@.h1\Z蹋pcy .0a:5o#d#d#d#dSltB6B6B6B6B6B6NOXe6qDQ۾m{ͷt vɩnzdC?`>nZQի0|Vq 4^&&3L;8SH'7[6 e9vꑭe4`ƆoC6ǃV^{9?J xq[g.oO4r{rٶUz=+_AϐmS xW75zd`La]^{oz5̬w>~D+djڋ\ɰΙF6 ԶO[Й*d٤&nv6c|9/|#L݂UTcɦ8 ÙVWhPwYHU3Մlllllllll=> 4dْLQ1߻7~hl߿=aC6e(s J:3'Ξ ]w-=tej7=J{Ǝ-߲-߲--I]>XooesΈ߲-߲-߲-ϖpÀocbJb>ss~l~l~l~l~lm-fBvZXllQ]V]llO_[ڂ< ^YXE`QB e2%ܜIqV:R*nj%%R_clg%M['b-"B}ܽdc:rͦ[&EM>Q)GG*TJ[  []!7iǴ%"% *sj.wIluwZC)z %{M^yZo-Լm\tQ}Q]E[6V_qy9fJsQCDֲ᪁SS=֩ !3&SSU+F* ecj!57׍eX1"5++uzX]3J/7\j̍IJAD;0nQ5G 5j.X']1L6<ʲ-IK0 Z[:nЖ t'.nIJ"&TY \-۬ ~dj1[ceX6/ Җ z4P5#&ڲ kٚ:CF_dq-wiAhkU .5 mfeX>2jt ]ce mټ0mu/ BbX! lVD`p,7]-/ :U=>2c<`Zn|aÈX;;#LZ]U?h^#i6+}I=Aڞ!ڲygl>2>d_M4pjіmVe Ϋe m|aAb_+m/ іmVytq&d6~_xX:\ S, ۃ {(Nr30l)Y,[Ͼ, VDϾaO<ĽZ6+ͮ m&w=+nA.o,}mfBDGyEGEApxw3$zIf~sSc٤e?2j:['X6J((eBNMB\VTrNk22D:m'tAYrӤ&)m[I2dgLȬ2Β,Nί-$y>$ 5rڲaiJ'(\R` ϊW6(]^>hJJ(i\j".=.9EI)h--[򘔘v;_6P]QD[6%*!YS%&ڲuE%F NFhVV&5 Z bbUC*WiӴ.J^cF cEze/mDA"gmD$ &e˯T'8[N$s@lZ&i&5KSsR9(I[*lYtܽH(A^1c Dz ffIK8-#?c&iA AFjHYԀܪ\ڲa`= &0Z6{P"/ 7Eh_b4і ƦǺ30eC3 0(8 2z>&&Iaq,.X[J[Lr0H,bwg0%ߝAt0.-RK[6-9G<*-ClkXڲ`n9ֲ-GUP0(֋D`AW&P feq#E$'DZDza0Atղ )(-Î0 4і &geuԜU`0@B83-Gc3mٌ>2|VK[6;,00; uڸMѝA,#Mtl,0t@ڲ+qALY0mٰzalDh :Y^˖nrDк3-cـwyCT;6W,,mY/(y9u"LC~gG%Uݞ=C"ȽmIWܫese+PfM!;~u˜W=A ^oհf%xG?Egn<ݲ:tHh[S՝a^sL!) ᦙnX8Pɓj;e2Au[>`}vnN2C qufn{O{*>;m19rE]{miprAQY%#vڼ,IVuO5l`_y=y,4em> Αp\oD!0V'l&M,n * `X6E?`ݱmط.LkG  V U7mC8LѥAu3P{oFAۈfpDZXX0ѝAee 7Ucλc:tn|Zh7m6bѰ`:DtiAK8zΝ 0hb imH,Ѵ320H,ۑSaa48-ם$?fLb|]CJ5ap_>0XZMd︌j-+kXq|[ V0XQcݾCAY31H%%`0]^^O,gn: ڻIJͭ*qe70GH3R|g,fpu31h2a ;_vWVs/0 kڈelV.؈erM9́ &iޮj{[ \WŃAQowA$[m֪Uѕgګ]IJ1 zf:n%-4=##sIIIDMٸq)))TR-߿Kiii)".Z(333Mo7~ꫯde2믿. !Fe''$DHcQ/I$R󫟾z뭷R,YL$?|{) ZO>R&?JE2p\)S0'|ǟ}XE,/sΧ,[L.Wɜ|㷿eN"?!F+cy̌(|^Ad{7VZ%UJQxVzo^b"/~I;74)%72i)^_~+&`믿B W0 Ky~~ " +.I=?obY.A\1 2<<>] ( pC>c >G0 s)REU?# Bdt3W_ALΔF 'x`#' DZb \r *UD:`QAfQX7le1 /.0/ܺu+ N,"Awfb`?` <e9al̝A!0s0H3`hh(9s愅1[aL3Ho{ꩧbbbd31@8<`0CO`*ny0+x ALl8oJ<w={\6O>uodÅsvJ^6l=jn->FWlŒK[6,p7tֱ kG~9aoq{̣ȴGfdppp 50{"C7'%%NX7"""J4jV,0=;i,PALD9b*,, '&&RlM\<44 yRlڠzVx槟~O Cn_7!^/نlԄ5hᇯJgбbVVX|rB"[io;|#0XRgj UVDA~gk֬A3BAQH}k׮E/ &>CD!bZҥK7n܈AJzw1P*1eeeo֭[Uz57L x۷o`"PRPPc b^4JW^C*AX N ;vM(G@zd0## >}0yj* 0 h0b`0::f0** WD`/OHH0Ax4,3339 Λ7 2%J b-Z૯ UbAL; ;F)   " e<  "t9 ~'r?D.h Z0s\r% e *CW, D& 2i>2]~}VU)` 01F0!sa~Q^ 7xes0( f$:DbPXyB!`E]'ODAGK[rr;4m(W]U4nƝA/tgp;" bwt,hX9 BB2TDĤ3%aPʌ4s/qg >B?;JXq2/-۟lXÖջVyrycVlپ;|73apLDD.y_O=>`}5,[H}YhɡC0mvo=Н[k%f)>,ľqrӑ#i4S㓡MM#'NGefMNn. : Sijq5C5d>ݮ /.[XRW;dw~Iq鎢=gT6eXX]7toQYnJL+a*̃wMwrE*W/,غ(KGTi[tR|$ꈦRc1׌wuy>[},X<ԬTNĞuQ"5{{3K3Oş²oĈi; &c]@ ľ`1ft5䐤dgĺ-#o%K&5YM;k Rϙl&SˍH1mK'jk*劅Hhbn141Bm6޹ta°S|!ꋈOMHK5՚~Fϙ3xSĮk] sjxGtCΠ$aľbdۉ nw:9{xܩTQ* G ^j  m5DY, L&l]:_s! JkKY6N=pIi++`v;ޱ1}U -=0d|W&"֗zdAS,[ `p߹},./67@W~RU)a" A pSiTMC&j 2ݪt]R4;cAZNEnF#K}nэA2ȊBQXLb0 -01Z{wMvdbJŋQ( +Є2e0h5rL$ML31x2$47Y[и4~L 2!07ϲ 9A68LJtgpo^~b bF$.3YaA> pl0/78AlyfXikmd?2+db샌/O `>H3**+SUu6QnAl41`^01n`4΄k+,P-0ɸaxhVM&dx4|V\3giDDe⹄Hb0hyAeɍ18;`xZwY1653&9iVoجXs}f q8 `ɾIg OAAǓ:l2ȞE:/ r΢õ`Չ[@I O(iƴ,ΰdWge}ltD~H8듼o,}fp1CD>OCJp)H^r:4I _F.@O߁MC-")I['/-e{ CJe[nDɔgɓJ9#󵥲r ""#ryY>'c\'rAɭd2&0(w/,.TWpDiTiVrա;y<$̒bI-)K\(ηs2F5.y׌2ܽ t}h<7y,1C1@:cjALH.kLg 1a BfN&GQ9b8#wyCYQCGO(zx(4kssD:b٘G @tB}WD!\CH_tRct͒N&.Kl;%XkipߍkC粝pž}׸2?4uc}/6x}.,G0}D2s-w+:s7xuF7m:oL]E߫Fvpy*w;ľˣeՋ7\)U{ͥoӖv+NQv+o Z':]Fv֛.O]f mn7O7tyjí[ʹe0>95ILko5NMM5ryP~Dͮqڲqe& eC $-vp=m70J]ֆAxbiˆᅈvLflG1qSfNL1GDN]jry.ds}:BeCdB<àѱIG6Z S vޝAz`bcټ0(-31nU_apG1Me -²^4ulcWz.^-bL Җ#F--6OuMNNM ~E[Y$k; W<9vecQ31H[6dy<>U}  ڲȵ' 1 ՝A̩QeA ñl0Ȗ᱉qD8͝AҾ=r"- b8 b1}ce m<2 3VƧ. Dmfe7:'8݆h6+ڲydp;^&'&v:K[Y$Ҩ 2"FQ-AGl_86E2Fzg=ҖmVycpr=1l%0Mכ,&#de"n{Mr Yݲ=2©8HekXyH+cMeC5":ĞщK8"Vy],۴bt`:Jw,ۭkOi[t[aڲǚuuejn'c$\o:?tyeGO;;jk=ecŚs#b1lӽf4Wď]]m\,DD}NM܈eC !a.;'q}KʣH[6yNMDL m0.ݚwȼzo[Mڲ1ncDO.3=/]AFtl2H,[ە~Ȭ eAc>2l0Zk}aF/Җ q&iVw~a._dq-۬ :|G!vnB[6 Җ#!uBhDHЖm&ilm|db~eƠeA߼8u 6+w-|a[/і;eA&Nn h?#i6+:0{/ X6 ҖG]wMecE/ vqcMduivl-/ :vޑ+VWs7H[Y$獶31l*IJW~7(1%1l--۬X6_Sʲ<CGFӖܱͧl9ZhkGF?y)X%ԧܵl0#cx=ul>;8:rK[ge:j|)ecͱlW637ɼ,ȍ{0mf]v3rj#e#Fa[-k#p:=W7a-,Ocو#SGX{΅{-WZzDz!#e#C0#wIJ{7Б[XA,Ƚ7yX{=8~X68BTA3m_:pàWooo?Ѳ7G3z]Ykoo۲?M߲wlQQK>]" [6e[6e[6e[Dˆ+}?B!?ZY6-߲-}[60j*дzjآ׿eS]R&8.GRTU JK,,~l~l~l~hgׂE J4u~l~vߖY6eKA0 \,Z/_Bq^o^n3?R ǯk&4EE*J٨3kBVDQt)|iS]:\*jْӖߗ4>OuY]c+&Y :bȪjeHIYh&5teһeS=P\pPc_#-E;U_TsZ} DzA\ tJv--]..}h$ZcE :w3:nH'~SopimVŊ [_m0Su.s!TDd-&#rʳn֩Zk˙xè_ѻڲA,,R:#iV5Z>PnR?,v K(WvyՀ"lKM爖 ~dfP'i0 Cl^FZsR9rw,EdјļsCDt\-b"X6 :JgG\-L "k;nD#M}Aڲyd9,־vH[6_d-[cǤz@ r>ڲygl^fˀb ⿴eAb٪{DMwkhAڲ9AYjת8;,ecjzeX6s_ "uUAWAw˶?>,;Hv'~/ bm-wi6Rg=#<ڲ152H]Tj_d5݃e m|d)#fۀu!eAb٪z=2qs :Ж;eABni1߶ +ҖmVyW?Ӳ,XF84'o-Ll##'Z9!Wb4Җ Ƥ5ydl2ڦ-k ~E[Y$\5-1)^ڲygl/{e<%l0)2h&e$-Gؙ9e3 XkDG :E[֛,,A1D\1rU m<2ahAL7AF1%&gUHkȣ-L "ifNvghp>ZK[_і3\kjcb.IAgiˆ0ɍA*qALY?"mٰb9a``.l(I0Тtܨ8K(< አugͱllway#@Au)eKH p.эg_|D zo? o'~ L'K"" \.{I3G- X(Yl '}gN4LuY?WЏY}e!V-' t|~ˮܻvڼ޼,[```}O=7X_*Hﭧ-;2:gj{#ˇIIx]IJ?I ;k*wjjZqjBBDfqhܶR֞wf)EP6Re5`A;!\!K(ÖKk5scQYQͰSs^M?zFT}ϻViT =IJ%W&oػA]8IIe/Z>j'--W_u]E57l6 v:0-JEFm fY엝5wiIJw۫Uc {roMmMոSHO <^__Ogg>P5Og>tPCcCSYBOO lljhF۔,'Coli]q|2dCK(8lSS}Ȁz5gBbCmmIJsl7lC0`aM嘆ܸ{qvGTJT}g&t`oolZ6̈́f[:ݧ̨[NmIL뮳v1R=d^2M6'n%C%X2x6g LQGkjeX<2]7PGJ^C+|SF,ˠ@ΠH e)ӂALWWm]0HXVo[SRZ{9#v#IJ{{dPTR/mݾϝC[LSCO/l,ff0"?0H,[?}ױ] 9aC!N 9 B8#`jX60x8w׌=y& W\ jhmlD,G m `XaGCBX  4 ۬WӡԘ:-G 1 w[c~p=GR20]LᧀAr\,.i´&bC8 |gw-ܲn|)fg 0(?ʲ?7f9ٜ֬_TP;l\.QtXeАvru9$+AA~Ngk074Kvb<`pǑ`겓AރU*L5W[[}X60x G lgR<2x39\};8zlIJQ<s Z9pt\TG9 '776wLp##0Z;N]3iqʼn79=:QxLw>}4}L%˶qLI&uІ oڴD7-[dH28h۶m'bϓsܹsgF6/:F‹ܮ9Oٽ{w$D <1g߾}i4V KK{#=r"l= Hq͘tt<ѣGS$)DN8,IE B0=ЩS G=}t$wm3gNHH-"xs%Jz꧟~:,,,yFs7I{gcbbj?|\\\$&/&$$I∈]parrr9J_/+%u^{tQAk|7b$1?~xb>O_|ΎD,YD,G~tRD Ku ԇ?q$\"(RўXO?㏉HB?ϖdÝ'|"$Bl'b~ٲeB0LF7sD)99GMQ0X[bPq(+ N+W%|"bYxя?,+#@fXɴh^}a:7Cؚ)`yY,V ͈=y:sg0?f?#;2xG@_ꒊmٰb Z6DGϛ?s5E8"aE,;)?sԄ."tw5|I"ψ"\:4@5E4dyD {M̬P"!VCLͼl{1V|zӨ"IM|À8^]ks=.5VD0|Cd.b ŹLwa0̍r1/ Λ;LΛ{,_Il?yT yc>+wD2>4Ƞ#A܂>+8o}0$+^E/ B|a}Vq|۲O>0BΠg0g]wYt>~`Afws1Ν?ww7_$}O &|bge$;>H3x'YW}8ϴlyy0h0/lyWz!ֲa-^t$e?i"˖a@`e{~ݲ=9ɥ_.e#m<1`e>,[@@@Bau!-ZHĢꢼ^zرcZr|'O5X\],ɞ}٠ Uȳ 9%%b􅄄jfiu@ pܹ {+V<EDD,## QttʮbT1UՎ&>> DoXx!V4\QӮĢ9d@ 2ZKDٳXD"zGgyF*9:~#xrrr08lM_xRadEzȑ SpW^ySE`eee&f۰af&et %l7o CeW{o۶m. Djee%Kv܉UXE2Ȉ6AnNe҃AlB!A&|0ayTTa" Xrr2LHHď`BfL bDD3 % 9 !b+mX0'N { 2 +brJtw 5 !&1m31tRĞo*00FCkKfYqʕ`A|3$_ qAܚ0@d*fDS hw͖*U2h00D1`pǎmruI1XZZDDc #VB9 =t 9s0(EĈ`F'i^yl<*0p 09 s0A,&9Ez-"sDMt:eBȀA #&w1e8L&QS*lz)DsD0Y0,B!8 "ELU8+ YZ<2d\6]n. P T]Z2 b a7G,;8o2YD,P",VAF &r 8>XBĢj0[UB &}&ٳhNIeQepĂa 3D_,~ll2Fu0Gew[c)!Õzx}5g+?Y2Gn#de|tLh`ٰs^]-m# 5H{l8X {.7O9.I747N77>)X3Rs$H,Cy9*5ހb-cQYQ:6Wlw?6lOa{TCjy>Jli,y))VD!9 uH)<&LjX0.ث-_=iá  -͘^*0%,۲utWuk/GI]Y>ݲ_><1V s2FUrs2>3F^0T wg uI"fDUtș@:c$ ;o 7eJuV1r&`%F B`-ߝA,1r& 'c$?6c;N&[4+F fIGgAfs/3FzfpRIdhh* ɝA\Y{1#`1imAc048 @dm3F4 2@1G7~;XFFg錑 e&I #X9 bʤytH #gaNC"l2d̷{g7 8dĠ߲V<-b?8[0bx DqWD!v\qIKXKР2KWMO,RwZ6+jBM#eK3ƄS%l32ȔT2W&nVڵ$^T1H!Mēܘ$xe };ReCd͵A4tXl+[V NAl&d(ჵD1"莇ye lU$28O$3x@ 6#la&`-I[Q^j1 ~+J / w:ե-L S6g4Ac(dl+ `D4OSbEyeA6|PPǞ./?r>mm<4~};+/9;[+=$n͹5U73g[6tV*ۊ+tS:E2j.HOW jKlLɂL)9LRyeԘbbJV0f)S)M@4F`eJÔ L 82%on_a 9#zs=O zxeSIԧ<`]'lj32EF]h=0߅ȴ5P#Q .i(#:%ec*#i-WkdZm+*56sL+ڦU9"NTP6Hxe9#@tWcv@F8eS߄6sFt쩾P})l2")3t2e=>9t\\o{~ʂuλ5`o(#TH+S|1 74`ʦ |dSF'N eS?> hjP6qzS(P6lBل eUo#&Vn.M(PY+ eP6lBل eV)M(P6lB)M(P6lBل e&M(P6lBل e&M(P6lbT&M(P6l&M(P6lBلS(P6lBل e&M(P6lBل e&M(P6q e&M(P6lB)M(P6lB)M(P6lBل e&M(P6l&M(P6lBلS(P6lBلS(P6lBل e&NlBل e&M(P6lBل e&M(P6lB)M(P6lBل eM=t9Ph&M(P6l&M(P6lBلzNU6W6lBل eP6lBل e&M(P6lBل e&M(P6lBل e e&mvGdP6lBل e&M(P6lBل eZlBل e&M(P6lBل e&MBل e&M(P_u](P6lBل e&M(P6lB_ل e&M(8 e&M(P6lOlBل e&M(P6lBل e&M(P6lB)M(P6lBل eP6lBل e&M(P6lBل e&M(P6l&M(P6lBلblBل e&M(P6lBل e&M(P6l&M(P6lBلS(P6lBل e&M(P6lBٮ &FPpEAPhBل e&MBل e&M(oӱCBل e&M(P6lBل e&7@ٴ 2-2AvMe&M([Ue 6r&M(P6lBل e&M(PklSp81r)m*ix|lJ$r:.xgSVkcHF. \.qghX,SLLL&#JS,2S 8Q2Q4yhEF@A.BdMFݩ?)”XЌ$ \OeMFdr mhP@F2S-Pb,2B?P e=i54L"#j7ȫsA'No\bq &NöP6qe&M(P6lBل e&M(۬M"͛'G(P6lBل e&M(P6lBٮ\~D(P6lBل e&M(P6lBل eP6lBلmG#ʔmoG3ĚΟ-p2w͔VYFV3F>;e;gϘʩʶͶ([veVKvjĿ>vbȷɐE95T7flqQ?Xd)NZz޷W8/֏۫gq05!z?8VW7q‰xwO2"rNq筶`Gn6[/;喡@<9K5=~d`_}Ƃ{줁)ۨ+zp'Lӟ}?=.ʆ,DnEמ^I jA߹W6FT/F?c:i#5>Mʦ&Q6g2sW`P7/+:3|v@k|+G3/giNp-&M(PWg{e&M(P6lCK0'+(;g' CҠs" D^sI Yl,~|5EGtoײXG'V_>ed}6r(۟y[;zIؠW\O>Eg88o wQ0n`ʆBX|T_0e{(d=P\|8L-eZv> 7Ylx]/<%䋰0 WEH/ŒKsEg:WlXH1~ f޿m麋6J9F9+%;DDj+f"0J֥wn>SAQaIfKal5=E b`: e0x&Fpɟ{Xl_^3ݖ gF_o>=U_XUg^h{.|4(?.h _K~~ !#;ڼ,FK^P6lBل eP6lBلU|q.p^ 4rWs}e;1{LJzޚ>lvO71!Gl[ޮُpB^וw6OJflX`ZJEQ(9M\MQ6/]ͳSlnr` snq_ғ8 e.J{ =Ń0Yx%ml +3+p05[@y`mS60\< ڌ*ɲzdA/g}vt9%Gl O֔ɽ{PlBل e&lBل eWM_*ۀ=5}}2c^7=1E}y^ l$EGt=,b][{UUp68fi[:ʶɰ2@}ۥfjt+m3A g8A`+X &R_6c'Koa~e4 e_1#w ϊe E/ꂿ<ݑ>s"]u1墛UMFh Ae{=0ْ,;ڽ7m"嶁"|.xN:c,M(P6lB!M(P6lVwS:#ƭgѭ+M}F.Q6;xeSsB-|XYm!JektVBل e&M(P6lBل]Ue3uAz e{+[*Sl9d& e&M(P#+b;]a KBIus]eF^jW豧uz/M(P6lS_WFf&M(PEe2/sҮnWP6qT=ǖgٹ^Ϝ6KV6?緎{,'λw e&M(P6lBل eU.C=`cq_lNq6M;=|DGpc| =9ZZ⣺?y8{%t=g5o)[6[ e&M(P6lBل e{+&7O/} e[N}gWC(8(ۍ[w_qsqZjW-Gt:Pq^%&umͣ =sTvv9߫Gj`HRݑɓV(KjG.J+R.XEp̚;sw{0ʂέ](zD,NNZ'XyIG$3"RqY0_L,, @*@eE?k&_4ay0,N, d$% r/--轉hZ6Jf :o"qO".OH,4Z ŧ{o 2e9SȋHdA?gHF! RbZT2DPYQR*:_\ eA4yW64|2h64Ȕ͠}**4W{nhh f2v S6[ 3RL'P AlusasV2h $  &^( 9E:UW7 ʦA"CR`0Af:gAޤV䕭FA^B*Փ4U 1I6#L 4zR eàB*֖2Hvē+[%AlȎ/Z`_ L0+[}ye͠rpLƤ'([A4<^٪2d5btUDYyt3+JIYD8V\%IdW: AJ Qeol䕭>aaS 6#Ln2T#(e nݯ]{ qc.Muo;OOS>-ӳMlW6P.yo8o( gا~"y+>Çu7We#A[L%nsDZO|ʆFF0U4RfF\ 1" :F, 8IJOT'xYօ)}EAFq?7*cRA-T m޲AhԤ`A61س4%?~*)e,xG5x@L?31e (7!skܖ<*@R8D%m)1Cƕ&A̵L[I0fS6D)dCj RbPcCF%n"I6c?q9F:6TaxB%l%1QeO>fI rf45.rELeҐ yx$;`Y .wpVA䃡X@ldg%eeCAlF[~X*`n#OxUe0ƿkdF2d"l󖕭:1`,"A%1 J)`2QL0HaSP fVG)-XA S63I`030{Dw%S JfN( 0X(dpJZGs$1h iYAbp|A~ts-E&*w7y8`U nQ'o.5 F7Y2c(1k1A*= T2Xcpmxٯ:5X e э)k3ȔM DkAkq>HWe*ʔMb0'Aq =^S }JZR6eP͓ttcZ bcc:s63yef0^ .wT(N%c ~MǗ-ܯYfVL?S(8('O[,U;C>چ3*ۺ6vɻlsIxR4߾ldPw`zOL0cp~\O6 X^v\a)|he Sf(weA84`aGtA-2˧Gأ?]~>EGG/jiy l -9G;'ȗ77Il7;iKyQ7C?ƂOMShϜ6wgj`9xf,Y;T f FΙ1 !XibCĐ%n] ςq˂pqtQ8(L^(ՓBFB䰏OG a&==Sb&'w<|a`u.nx*4E::`' ف-+ >$1F. 0WAzZ]֡CLJ*D[ 1hQ0xl0ɭ/\Ȁ`RzAmƐ$T1H۪3K J7w8F k/ \s02$ * ^ B'O %tN3Jw4*(3X<1`^ qA  `pLbBpCAN.gA/#*j H4:$u4h $)J4 t330Hz>Ä@% Vw2F"qca%Ou;ݮtϠZ\dtk3ح | +O3ɌƘsJc A5n3A "R|O5i͕WchAY6ekҳ]p[U6DP)|W3ؿuP6q̠l=rTIZ)G`1y8UGRwv7_쩥l齋.wso;l7ܞn=SwW?0y$G%$xΑGc\;wpm?r܍~ot_VtܾJ~|y (ҋ'y̓;z!i{*[/cA''w~jh`(O>^JO_~p/t#>sy~W~BKgТIkOO 3|H_^ٳJ:!h,z(s>?/P;r/D5OIM A6?})Q(%4-.ۥ, ~<"|Gk|0H#h?\#1 ^q5_J`Rctơ7 e- Y*?#t޺:(R( TS ?t1RAwhjf?'1U%i9i] T0l4=dp%`p"$Y_ L%/Qh"vA]%8q⩾Zo~iA<j oߦdwE~H '̇э2 l-`p9>/%,g_lR odpAU25j4ATS%٥A4`T7^: vѭ*`إ ?S6TcT72>MG5 ~ccUt%J q 9:3svWapa5(`bd_ e޶} V(N8chLP~şsZ(8fT6gw,<4>ῗߴmRJüu-ʣk]rLO/ &rH\K~os{k]vҿPCw2n<> j[,pC "|ղV]iÇtwo-<{պ`)q[\c7@R BI~6 x $1Hn#A28V05A<NA9)NJQhArq sWc b([ofnb "H|ZMX`oERӚObG Gwa " ~ud[0az.zNݳ͖ A~ ВL+2wʖ7u8Ѐa֍F;N ee9ES'%w$cp.;Qp9 Pw:.Cd#G۞j6{T m^k1yGS!xA{4*w&YtC(k,^wcdNG]1>%|Ncsñ"Upp'翶m -O13ʿm$q#E]',¿k%YJoB"{M뉣j9 Y0ﹸ C0D3|%Mʿ;g;B0#k d>2 %e}`h*h|:1䋥>${<9LKuS#~DM4`K:>qf[3H2Jg3"U;A;DB 7 T|uwa\&C0-yxXEeCCE _6\ʔ5J >-`Eo0Rܸ -_s ۪0Hﲡuap)-AqVaPS ҺDF <v #26dscuf&BE.[U9l{1r< Lbh0y_6D'2d]I.4Z ҕmp.U1A4l!L2H\x yY .{L2oE`00q { U.m0C<SA w을A'^]/OtNɠ% 15tػleaƁ:j1T0`Bi~t F (҅ p 2Fu{&J`@V%PUFAMM~v.99Mm%doeJ NIb\8U2+.9oTec8+d & `CO3DG`̈Q2t/7?x*-#A^@bL*Wd̓0D4c Pk>G2I0bO׏Ղ袙 G /,gD1A.Q>ؘ]D/I1jD3 "^̿N _zo]_ˈaI:g)d-"sdZ3&QM2{&R0a9l"/ҫR9awwy0X<'*7R:B)̯*1])=ĭ G.>$8};[~ADA~<*(j)4m4H$h ɂhulE{@m AޣZ+V='K뛡yApȚ跨es6lW3+<-h!ZƙdƂ %KjCDgeƠUN1WQK-`O.OUg-_!ԘrH974N#ÔĠ9 U6~~A5%[5Q`AD7–! Fj A܈_c)jl ӕ 4L(왞_a.RA#70zOU0H?GiC0fΫ¨=1जA23ecU=q69aA _-E"Ӹ`(O * V0bw҄A[5dĠmf Ds  j ٳG3/N(j-jħuBG)ی _HF! %=i(Q2) Bl3A~IhU%dF"U6l #VT$^AX#AVI%cn8_;#Ʒk /8v |&McZ0Zm_vݷw M.l?g醁s-?͇S rW•Atb2+[S.Ⴜ*p_H!.T㙂b },,H;Պ~i HrYJgӟIG]b| H61`QMEA>%F k2*pXIݾ"?.Hd0!c7U^A4<~28rdG-$x|E~ NqdTPn*$2-?#WP  @%#tIU NM;qX0ŽgJS&6jԈQP6qzXy:VÎuyekhJLůV*wv[iih/W\rF. ;uJYjo\#}C7n+톞 -\ b[i7N5JPVڍvA!_QmS0uFޯV =5JQg6`oFVڍ>` Wo,~gvCOFӺ[iSدVڟ\bkiQkD[iTeBjV6]!kW[\.XCЫlW[fl,22 ektfFm3;ektshHflHC3XEox^meEFkl5P6TGC5j:h@f1ߘ t45VMocYxaMP6q9=dqʦװwLSwwل e&M(P6lBل e{)}:@.4P6qT=l7n'Zу]sbIQWl l.yi-SLjz߽P6lBل e&M(P6lueD'=nlPloac=7 ].A[iP]28r崲mhh]s_nʶqݮ/icfmtH,hp !eJDF- uSF-]\QkJP)~)iDl)_HLI[&RfyI'o2%Nl$&Mœ{\BtJZfA Ulh%MЦNdDHzl,Y3$YWX7%[DR9^ْY=bυDF r+H34ɞAe;)-<߅Ҡb7_E`ʆ's*Iƽ|eW kHD `!7#@6ȢK+OH;iwz h"AG狊`-l-X FC+mQ"w T(ی 2e32h LBQk0+J DaKd0U@,+*qFE:Wӹޛ0rdЛ2e``0Z}! G 3S- RZ^* wjfT =)ʨa0ƀ&+ePh9ڛ*JѰ1) ڂGlUD'jjqo"';F{L*3ȔM A# V"aAS S$Q}~UgPڷ+Q#ݴmhnPc I ʦARV|>:Zf0dDfye k3ȔV LI,EBuyeʠO =̰9pZ ҹ(l32Ȕ#+Ϙam(e \к~ jÄEGtBQG٠vߺğswJh< }gt }l-P6瞾|1]-y+>EúgNVTPlsS\qu:Q' ZiltO]an {}FtO B  =: )=IIpAƀ-Ǎ t BKӔX>p@;?8sQy} Sd;5LҔhJ63DϣR"; tw}3Ժbr4R)[ *F%lΘƬNmL+bjߋʆqlIAti$Ji_2Prw&d6 A3F_nΆ*/]D٨ )1" _V6\Ƒ0cAQ %J)\ ʊ _x`T r!km3zT2G)`H ns!{LCA S7'' 011/HdPyza{P28VA1Ӵd4WpMCri-+w: ) LlZ[u)(|/=dP[`|Ԉ6jh`jyzTD1ݙ#2xI0.>CF@ 4:b _Е1WJm H1w>kd>%cOc ?_e #ehʢw:%FЂ^k P|sxGx&VmO@ѡ=4 jL#=' (+ %r#OP% *SF?nĂ'}O2k)q;ndž|Po c$ysGhi6)R Y 1tO5^`UDCEbAG 3<\V[:*1s(Î"Uɂ[/9)q헝4G1d1^yvv9YJ#4W\|s)nY'zn>L=w| gzH!#qOf !WFSO4 zy40h2A44lr(1\AtHS48$ $?0~a\<@#!5 pg¸J+4ll`] қ}G3(v(͂$ݲ`MaCu=/)R52f\*DӢ Ҽ !tW0Rk 7=ӯ#;4$Jmhຶ28m&b.9십AD,3B #ƌƜC31şZ~}.f̞*u1=enU(xI۬bth;+Dg6mèAaRA+aM 5R48n`  Z*-1Hו@ QA . UɠmL)\uΌ?Z{{܌A2MdFtjv0.`0`Ac>K vVap&<*|A<Dǂl1 P0< N;AfAte#BA %8q k1HޞlG0lXGwʆ*;5M'j1EGt DŽw;([2[bKJ_-e{-Mo;0ni9 ~ecw}y<3{WEwΑw.hECnУm_woo?Xwa$7l-]Tx]M[9>2wsHnĥƁoI>O "_d) >Gkq#>x#?R޸ez# ɑ7=x\ʛ ^E𗛆bG߶}^A{Bm4_ށb oJv׮!?zZu遼8?x=G< ~x:/+:3gPG,Ot޿w+I=gϯ6)'/azϔS0=.m>|.Ϳ=q -?e>D'ʮxw,h#ѧʮY/>ۅ'+H{ʢ#XN.WǖAy|Gka9_Q hnde4{u __s׎14 >{QGy<:2]AASKjj3&A 0d݀"XA܋2Ț OrDiϬDvj ~]۶dϕV00Ȃ|T2e ӗ|F4F%WtV2)9_Xye/`Hu?S s ҧʳ5|A{ _1dR[0iԬu6k \+%e] J?\ۧbw_.Y[ H)5 R?)%lpT0IiJSe:OR{sO+|%>.L0Ha] i0% /SW0x0Ht FJWI .)3Hh}ޝT1)뻹[UgA<7_[[Ӈ F:Ĕ &ަ"Q enjF@.b^k$/>yxێ NK Qh*p1=ĵwn>oӿeC6; !l_v`xΑMFj`u<ہ;w>vÒ# v6m6~1\wQ,ñ]}{Ay+z$ˎ[:ULاa)^rp΂t~\v0waSf\rmϝ1ٮ,&}rpeF̯/[z8]MX]W\fsme(j; BU<4OzQn{][/l--=N2,c6(|iCw^]~@^7=;Y *ӏǛoxP].Awbg{=h?^߿b:ȀM'/pu-?N/X zbXNZ sA#=yz| |Պ`N‚w~ $,v'AƭC4E =ndm, 6")Hs6T1 @A#e4V`25xŪ y)ᆖJj%$Q#nuTc2%A\5l۪2iYy09D3ga T2 3a½'k3x))FrT?MNv^d(amU;1ς? dʆ$tZ" {a;rhCsheH9d)@rcN$SvHbhHRKHHXcsC'֎`K[Noc_€e'ܷ Ns@UpA#=ޗ0dH#.fwLeZOG(^Տǧlq3oAځ^.(^87_(FX!S(L1SZ#W;ýP"NP|)|jI"{1<`I:Q#@A%勺Y2x~|D(Ot&pA7x$E}i˂}~EJ<02i3`P<1d ES4-.~2{;ȀG ~"Z_s@>ܡ%X"At#L:=]6>W5(|n8/e'pS0И#"{  {d)<Ĕ• 3 >L+ $2 } OE238U_>ݞP6U28(1菕p{)Q|D.P2`i [}0( ܊GXi 1آ Aܓ]֙+۠RG, )|67ֵh`0W$ C7Xr{.{91>kwj1SGT;Ua_˻l(aی 5-ł pe F: T0~OpA9%#7b>_2k0P2x|0 ٻlhE'*D#iQs32خp"L " Y Y d`VMc0q#.[Ac=un])L ND!e0Z~L$@UVa\ve0OPVLPQ2t1`2ܛ*Z#$ӏwf.8h!_dN00V|+۫h1̊^~Ry eiͦ? {5|-?M,v ~#,$qEpܓ) G"1{AH1[빥2W kؒ }O@i $#AŃ p%Mꌅ^KD"oA?H1eC=|~0Q阨e=)`2*7%77DSzgk!Bf#DaY +3H:yag)q!vq4ёcʆ NA,&]Mp<;so#%9 a;aL b$sG)UnI lނ;'dr$c7z hE L#11/[HdHT25i%1"'4,[ɂ *-?RA j!Zc$S w#nO- Lf ]sfQ`Uϖ Va37069ޑfFl%AlA9`MtT0# bɖ`4.;9n (іgF Κ0}EDL l$J*DAA{ˏLIf6S+F738)g cL 3F7N:[_#5,$s両oI QH1ec k1ٜԈ_bЪd0^ x!lpL+d z"iHMe1Y|U *G@dg%DtĠ~ft>dx[~A(*tp ~^ĠE -V,3W2o҅|:O*4A0m)sQJhKˏu 訲ݰy 3lYP6qZieGT>=>. yl`X&$ EcB"%",}_pxFHET>Q2"|\é<߹Q sx1Nd @/ODut3.L/LPT@\,-Oc"4XAt\*t)'o,韖"_c)J` _~#GyyE0Y (%ź(I_",ȧD털 ]S,D w `/D Q,FlѠF*pCkW`U NM/YJz"k0Ȕ,?Aj:p.U . D`w`Φd`_>S"*$ da W{5LdI~ NqaE H>a)/?#SӋ d t( l: Z:.}IO8 4<[F߿L=ALP#"]_%qf'QZEgdՈˣ b0+="}EU2H*(VGZY~L _BjLJSfp[*I9%1^A:#!e;*}a ß}{9m mN)P6qzXbҭG] en SvTl>͋g6zr%W{+mJ\V5jgvCO¾66>LgvC=5Joh_mhJ57ԗn+톞 6r n/ES[i}v[i jl6` WoF7n+톞 ՍuF%`+ϬzuLJrլl8^r|zE'm6,>W[/ri]UU6Yt*,2r EFflΫgFm3;ektshHflHC3XEь44"#W[5PFM(N54kloB^ :q|dj+&N 7̱>Ҷ"ϟ5nBP`l%Z"M(P6lBل e&M(kT٢¨3 {F,c'&zfi h4ϖ@^¿l;ke4V4U)6ۏz e&M(P6lBل e{*ٛ~䈾MSXKBQz~nmbC*]~BO/u+*ې#zϞ1.D< $hO&M(P6lBل e&M(kNlԲ՘׿ nBġݶcȀhE%%c/~XЫMoF]Nvg*/'M}IlBل e&M(P6lB^ʆO0eN֦].lPl?^?y+==[v\OU,J쒇t:ʛmM+[:+lGR˭ۇoݱsKۙi~z8CUPN) A{P٥"‚ SF-M.f&ueHLI{yX&RHBd;-ůV`)ziRN*zJ6'-]1|ϓ+ ˔zʖu \qܛ7!O,NlNxH.l mWw;834;򣮘bx:?)?](>mW,L"I39R, z hBD{d’~7W6ޛP̌@uUQWO,1w'"h0KhohutdlHS1Ti8G, d6#L  VA^ِeDu\~rȜ42j╍2 U6uW0wm]w>gg=';g'd2iNu&LIu$8e{'NU,YŖU{e*TDRl`N4DoAp]R9nwT(o:-OIN|6W WQ3LG7/.V1+[nyel#t05 Ebޓ+ۜ 2eXe{)[eʖ5` e)[&3A.HMm `Ql1ʠW 왃T$rcrIBӝM 186'EЁ'E D yәAR7$}21H:D]w " 01^P fQWkgcMM=mNye# }k)u!{1$+y.Pܿ]@?͢gTj|#O%^T2$Mr(/U?_&'ck+6s+gl,>GikC^wM=.K#;uQھtM[8sc}Fg)n))W &G{ jB(&M)s=$C5 :]0 QI6d D;Nn }Ӎ,A,9r)RSʆ(n zLL+kb6 |VPcPFC41F6O^nB#3flĤ~1F!)5j1[,ÌBDg/u҃5~U~y(#r0挎ZDdA\ )' dCpS)ssxGl~%P49a~iwh|(D+|0h!k2e)f&nDJQHfʦL>)Ȧ/B9Ep 69H 0e V ~+YTƒ dd29ɔAl#nG%G]VcN90ܘ!'ֹk^P~ vJ)eK2d0(9ŔmP2fa$%t Y%9.ue$_į ils2ȔMi`oif 4֧‘\ ƒ "Ȕ2Atm fʖ`jv:u1x۩ Kz{z:9f0h!`2e 鈖)H|Ln(S 1Ơ[ 6 )ř4dnL] #n\?e@9m{ o̦lt_|`7Eʆ蕪ɷ=Q7]P]gjȐG,8 껂C&`TiSY˘nϢs|JØ}^>3P=6׋o;S)=QKF&zQY=䃹F3 *Lԫ&FMj,u.*lѤ)j|(&26<jjIeA|ʲ>K#XPqyt5*i̔Wz쫮0j ^ u\WQhJe}T9~=29"%eVXJ7rǘ՛7Wh0/Sm-ZI ~nhnuK5AY[ %FKszkprqM= jAu.4% 7R3n0 aXYCXJ* }u퉖1>8ٚ4B6j% J>nS_&ep5uIg* mt0hC3x঱t4n\֗[3pL2Ea ^ÄA\dĜC|:s`pi SAK vg0U Zc( b${g$NSv7<ݞ` "{3x,A[ā 35d2cO?R),ERf2z0Fmr DРb8أ%Ogapg窬gaS`2V4&|#AK v0x= E%C`n^='g# fepƒ5CN~>/30ge1sAn-}AHO0H}\~i3AQA˂%QW<^ O  0x d bO6ga0`k{HT1 Q[k(r>_41mifC Y_M4Hi|oq4~ bPWz5W|E Xׄe?j{.77cuRAdkk \|ʻ7V20\WnjYxv6b ѵPɨ^8g%?fZps3E}{:ܛ/ b]r{'S hO&s Qß_!1- ?|#o`Dv(Մ-9h|, 3km)\k3 ZxSWga`~c]Ń2aV|0`GU`pAZnl})-ֶ'`pAv{(3T~$L0rtſd geu{wuC;ڟ2< p 18}7҃e2ۧar`o&2 -ā2_A@Gqe׵bvnzl^ ~k}3p2A RQ94ܾu.ϋOqٍ2x2lvl h*xpx*t<F~ytgVBkNexdhőFDdj>Ty8-C;eX=$Mf"Z,)+P;2 *#RhB%c)\v *,xք%y8KpC΀em 6jz ! ]H=eP1'F4O1A Z$ )ޔd f*&1(}bYZ9Ӟ  t}{`p5\ILVj+(<2)14 m|J0ԬdP&<{%Ћ2, "F2 w1e0X ' wa6I0k19`2͞*ӥn9G$Mg7tG dAZw- [=(w;B\L[J ` mYMQaG[ۖd00[e  .I0+[8ޚs)UYdeg Igp)s3 2IжR QWTНArz9}vy nLnL7}Rj(͏0X*1 %fvLoQɿ9#gkVv(9+iZw&'3uwNoB)3kuB+ecnztu9*=L9{>`dK%ϔ}c}3"T08 * McZt;cM{M^N[01oǂq|} 6K0'1+Lִc0&[ 49M[]a"7nLwFӃj[hA>u1t_f 鎱{ʮw/#3%D*[GEqiN1M= !lq2FŰ&7Kb(caw K,f۸=0oaĥ10btmK[`ƒm? |ɸzǺ96D17ǥ05w%ǚme8g<.uNlRn?JFMq7] JOxӂL'.:d ʶK&=tm3Ϡp,8ZM<)Ԏa&i:Q#Q Y}=YFz{&_cd> u5L `Fl J G,V`O&r)YjX ۏ`nn̨>>#c0A,u0S9dۏ8'e0.m"W;`3#HC~ GSLdm?&`ZpjVy9 X#~vDS+fe0N6#)#b`#^0H._U~$ G`F=ғ,drT)Ĕ1sdaIV0/` 6$`f2.gq#qiUߜ z5c0kF] HedʖbПIF K &G=㞨Adޛ ~c0-;`ba%dɠv䎑h jcM-ƠR9+#~L寞@민GvݿQ(xW^IeTȍR%+&A*&nL팴?d2S6L&|0kԟ(1S6w>,oOW`Atq҂LOH~: 򟲱l^K66MI0Dʌ`8wcTĤI'$O&TmK jgik̠S6&Ȍ/3B=3N;|`8-ugM H7G;D,At)[_l]37v~ O&s28 .S9dF6dZ?e n?$ȑhy1Hܧls28m'NAA)Nt({> )ۜ p`{blIJ~6&._̛v&9 @6ӓ/ll9i{0/is0Ȕm|<;`:Yɸ+rO&ht|ASR`F !?=ΓA)NZ'h2S zB & m3YA~9dvu/W W]SonzC D@0ƿB뽣lu7GiYV͔'b)~1Qy z+NՏF=]w~.Y3ғoQj2 NVy>GiO0݆Gi:QѐZQTH_;Gi+47֭~66z\TJ|7>W(񫛣>w([!v) cP8[lx.Spa֯fQ6{Y?0y= 0l}R7}g}P6lBل ?ߧlBل e&M(ۻl}-VO`i_*go3h+, ̞h/uQ'^ON*uoVϭlCE5UVz6֢Z<9Rg\G&BBل e&M(P6lBل e=uP廤5hS#M7mL!8ohq(#1('R8~[oѓnKKx(wPW72SK&Y9IybӘ+:}mLh8WKiFN4-eR.] ò4RpB)ǂYm8y]|X7` ]гnSRBل e&M(P6lBل eEqmU3YYB_B e[ʻA i$x!fCNQ;eDCȘgr:>93%V(<o e9vnEE;֮7mu64% %  lBل e&M(P6lBل[ XPj'SApT;I[~sJ}+[?v? nE߮"NSO y#efo(ۡV*kkMHWxeĚK}nO8.*7QjCxjuzQ"x"U6~>lPxv y{Qf-a]#Bل e&M(P6lBل e.l)K_8kL^hUsayrtB5&2ǎz;m?e,<^<椮[z/G٠!3[&^5^XפM(U8~T/J˯NwOV6x14(Q6U/=[T,̋nl L8F;79Z:>@(P6lB,t&M(P6lB;F_[?W9”= 8ϔ F 3.T:xnYz틯Y]J4x{ do?̿zur^ ]sV&)p%7/Z!c*Eu%W-Pg`jYnHSSOXzqIʠfm!U^,:7gp!s͐orj)]2Siy2E:Dp pU #S7+,[z{H9 FT|QVT-Sa '}SeԇYv |_ F#Bل e&M(P6lBلZewAVYܰ~zͣ >!kQT jZ@Sn=wAEN-=]kpZ\Qe+tPv&M(P6lBل e&M(mU6V  P ;GA e&M(۟D^+%G(P6lBل e&M(P6lB :? endstream endobj 39 0 obj << /Type /XObject /Subtype /Image /Width 1169 /Height 268 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 632 /Filter /FlateDecode >> stream x! FjK endstream endobj 42 0 obj << /Length 2944 /Filter /FlateDecode >> stream xڭ[ko8_a̗u5ÇHICmf$v,ӶZ=\=~Id'@-PRߣ8璁N=G8Az{$L}N6gN 7LPǟēwLPa[&hN\<&.&P/ՑiY<ć>ٴB<ɳ A*O7~D51T#+@no^~aezJmP7u@6NL 忐B) S@/f ohm<+>1JL&K2:\TQ{SS߽B@\6RWWo?5w􍘧r MQ쳼4s_ cx AQHnqy%t"dITHS;hf0Xeځ5F?XsQcPN|XɈRX68Kw(+K~wlD]a!Z5:L?蠐h$gBh74M lӏevbaeLw7nno/'覉F-²~h|l]E&@6y} +V,@؉6BqFD7"o΀lW2! 49%貪{wxS{yN6F~HoGưuvk*HhUn!ch!@(?\jeJ L}.J9jeD=}m$Lڵqb`FZx*,p/,1@[ta8 h˲@mGiݜ߭Vq*p0izDwݑJue#_5aPDD1̄ahhQ 3Rh˫N:nԦ`L#3(3^5<ְ':C ʖ7ő 6CX Jf4TF GVi^;ąnCҴ ͠,E$>e z!`!$xmB@x߅IL$R:A@oc) *oSpœMjv̌t[\T6iT]r#Ќ1ti6IyeiZFv֣Q7YSy65tG#m5t0JCxՅPPlVma)L=QZe|3AAksN1$¦|MK98Z&2C֨(aH}ŏYwtPU< Sh<j341ԣE_ZuTRMS]1XS$A?^hsW }r!AQDh`y - m,2}A3y4mveO>`Nџa 4Y~wE̓zğ.-9y Q| ZfB= C΄Ρ2;6@9>@D4#\0FW7c6°r1[q@m z,D)C_Jr)fftE-d0ʽ?{ӣ@2YB6@La JS(2>Vni!'x` c poc:Jɬ2faVe٢qiKw5K0;o/NџwO ݑV|%}{F\HBzsdug(m $ unv/͚RWj0p~\/d @m4RMLCH*IQ0tjMƔ^L GKLGGTQS:)ޘ懦KQ C7 @mc gHLfIr1j9D"yLsd6danf6v`Z?E6BMLjߕӚ&'N} e mbAݔ}_qhI1@$kQRׇn/C> stream xڭYے6}r" Hv9)'lyT(y~vQ'@>}J땂gbaċU*$foKok?xR4wk~Ad_yOITy /"1Ƌ-txs72nwY}=Q~O$ \דH>mqq`gOEٗR$+Ųf5 R1u~W&#2Ζq5D=6Э@D/CbXy>@Vn궨{[ǚ[3i T^2JȤFQH JZq\8&LsEIpzB5[]Ņ/y3iRﰢgՖ'p3Z\'p3PQp37l$~!Qaͨe_uH%eX-Q$4i7EW6durGm@,V1cyV/c;E!vCÈ0H=BF1!6}&%E 2P\B2Ζq A""8.Ӥgn'|_+#Nj级A{t%HM='els9ףأZIza>[ PcZ(m|p@Y޶`Wy(wY0Hfy{Y&0q̗s?; `BxZ*BE֯}.ʢ-5PiA|O8o0*Zw6+z[Z,nd0Om#cJtg8iATBs٭ϖq<:E1Tj±1@ +x&y5N򦩛vҀ=z}^U/ㄉ < wU ;GЀ}_qjDk3|!BdIȀJH(^r΃B5dЃCDK^et"6))"5W,tZoރVJA(Mh W?f]rګ(;g8V%f! ui+eޭ ܜ&v~0g^Y4w-@aQm]nj+, ބ.=QXpxXL4Tstls5oiV+ReQQ?:,.MdI޴SLQM/i&㊗m`B 9cQ,hqUWaBTEpsϝ$f{gPA&PP3 I)VpSϚ"ï(v0t^mnqE:uOa0j/Òj81{ - w]#aή@BAm΃b]Cm|P7C9$`+Hh: 7wPGՌ\ߦ*;HXli&2ٟZhа`&GQHeH q6|hZ96꺮L.8»ZVs.ʶ 잸nWNĂ=Wo:4CTZQBd:k( H&%\}N%|C.%Kp /+I&@KrXtKmMN${Θ>/:ilkw uߊ[ E~"|-},B^Ha/س{# RB֭8)fQ*kBF΂A 1|e(y@@4]w Zlh׆dy{.*r.4&ֶ<>@ ;ּ¾:76hKtp,h6aȉu:K 9UkG#acl|Muf.~P a 971Ϗoc"(hv.G(f[aPqۻ|,lT[ǘs~Ḭ {@ۮz׶RҮ-i뗕6Еk_> stream x\M, ^;0nZ{@0Di[Z7o}[>~;{y3sߙ;7n(QD%J(QD%J(QD%J^DFFƆ  M{ο[np"""⒒nݺ111密%J(QD/6l5kɓ'_ic ƍ,Yrɒ%)6$dh߾}Ǐ@ZСC(t{ƍ/8" a SN۷B׮][nݥKQQQJD%J%44tYfySsJ*h呑RL bۤ+f̘b?xn߾]F 2lp8;޺u^Ю];[Jpڈ,--3gΜ,Uի+Q$9W nGֱ-`%J[BBB¢ĭ[eD-ZI Œ9sdɒ@ׯ_Fg픣dڵk2eNj2]/]!_ڴN;Jqt)76 'nݺqDLbrD4=ڞxU)~6GVVV8~͚5^:E|MlJ( ٛ7orZb7o^jշzXb .TPA%JOp-QDrj׮ ?;! o܎IElllxxӧB4 UhD}?9y$S_OAԿs$.Ak_t mУm%LńN%lsw3JPPH/^$47^ʈmE' j9adz{䠤,F?Lf#sԩsQG_sgqqqW\ELG/j2te܈"=H=sLRR ق1ZsYԚd(ѵ .Ҋep=6͛={̙3=c(QD.w\ln߾[n˗ϛ77Y̙?%Jϟ?w| xVZ#Fſ3n"'mvҤI%K`1XEϗ/L0!5t dekkK_Bۮ]2 aRΝ5Pg RةS̜9jժ\DGUV@p(a&ML>{-+W޵k{` @(6mPgR.֭[3Lcr2 0TRBAj*vvv׮]K7bsƍg̘\R;wG͌ 69"kqg݇ &Nع_ѹpŠ+r4?ԩSg޽z,[L2 4'YQvڲe֭[_I IXZ5 mO b0Ծ}{CGуOhŗQ r>}vЁ%67jԈX@G6ɟܾ`23f|wt&%J5]K3gϵ+Gr՞¹snݺёչs缕(QnΞ=zA 4%.vj֬9{l_}n\ H0eȝ ѣG #2"P(.R%@p۩AG\,,,dVl椘ƃ@{ե Ҋ WaE6;'cСC#Qhmm-c*J؋)$Lb1RB.M6E9mAYҐ\W'uQQRv &x&zb̴/^C7oN)SfsfͰrfz6M8H=)Vfc-Jclر"-: LN 84TQ"ۜV aQO.؅u۷tLj{Nu9yz-Gѡ 伕Rr``Hp'FbÝ;wݻ-8Q.]NJ(ϱssqqiݺ5W'u ܶmۥKBBB(Q򧄯ܩeQDw(:tyl|K^FnܸQfJ`$ _QD\U_sbv c(}:fh v)Sf%K:TM4-ٳ'(wttdtp; q9%!=Z2"N!CBƪk׮ф*ݧOl SD]o3gf36Kl>rޡYggg;d=qh`{ 3w("2G&zOLgiEYG#G䬃x Njժ Kc픔-[}qmEvIW^}U\3@9{Ϝ9Cΐ߹sg2)Y#-IF /#?4(%JͅK+WpT`Ana[lŁW%J%v'sέSQ3i]Ej߻~-R\\*!>֭ #fbzn{re֭!2q4h_|!Y_}U6mYzuS [[[ 8r~>|@iXSaÆ(Ըްab^ժUDCc`6G3f_r鑆qzDe-8J۷ogrQ<]-9!9D×Mv 9̸$nx{ݺu4aySv\}-\PVȤDDCesܹ͛C\5gv[6v^@ !rɣK6Yr:cƌ:6mںu봫߂ r%ya e7P*Xu5bp;iu]n_ & zs;ѣG3g4ъ+4).^OjgxGɓJp֖]oR_L8F_ Y1=׬YUPs@_ ׫WyiÆ XѡԷD5ӌ,_jN C)_ZOFpa\d w+.\E]]]%S%̙UVA]ʕϞ=KڢO#s.KpC *d5jn1jԨǏDr%=J0vٲezEOsWVM#wlR%Zv.\ adD%Q˓ Ԗq 8YY BNMɏ;wR  MRv-ib9%ǏuMdɒm(IqMyk0tMR~G3y^߿/Qt%J. ,ȑfWo>~ Vѿn'd*r׮]4Qng͚1M6qlb^z4ud<5XvAw^rd= ҥ$÷@5ƌ#K*nϙ3eVVVڵsnݚYr%~Xb<]7vmE' 0GGX#"gf8s2/ 0ϟ=z9sv쒠@0 c~eC ݻwSnذaj޲hlbz)Mp ٚ5k$ޑ^d!l޽{(Q\ZZZ;VD?B/\ЬY3vuu}%Xbɓ'B]䦞>|hoo*+W'8x@cǎI\NzEѣnݺ:V{dvm%1N]PdSO4edPH9'AĹsdA=SB96`#jeIrAΒ=R\9,L8Q[ݻ/e0 6O6gI Íu-[p-=!% `crp*rd)KL-xڒ%Kb gQ|b-𛝝uxDiӦZjkeuJp/a~$z…v嵗xAI֭PmtӧOa͡MY6mً[[[#=BOYΑ˸ctŊJ&fO5WV+67jԈ@hҸqćlݛBo.$$(cd8x"2Yې? (88PV+]rp9FAԦd0C͸WV-K,+>_Bތ4 ЦU6Qr*ӌbVv!sD4)gqqqU&zRt~'))I *޴fټNڧ¸bRԷSDDdΜ+iW=OPvuu}yUck׮͓'Oڵ%$$Ŋˑ#ǬYRKVQ7k\OޠhѢԖ+*΅+>D%DէN-[6nnnn*uL\s%Boذ˓6VX5kJ*)n ͟??gΜEQOS$&&FD;v>[t?~\>p3# [Γ'ZE%o@_xhѢrZtJkO1@ՑPy4E۫>˫p\۝]_c+_01Fڦ](ZfNZJ5ث)+V_AsuY:ȏh ')r%www??"""vݢE&Mؘ ^ziWDW+۷oͽM6ç .ڌ;v&™nnn ,3f̌3:$pU߰aC,--,Ys;vP95E3M6M4-ZE4ܵkה)SkggwI`SOBۛAxbL6MßW\0g]j7or,Y2gΜoGe?&v%JKcÇW.K%zE}oR88Rjժ*]%J(G͛7!@JKmy׮] ,X\3go/^&!46nܨfooevԩSHݪU:_~/켼dA v!޽{ʕ>>>Tsuu]hB:tr{9nذ <ݻ7@+o~\2Ñc-ڬY1ctU^nkkNCym ؞={@ˋf*T0pc8{lTgz/RH>}FUN q֭[A" 2 ۢ Pls}LDжm[p<*ͩCG=I"/[8q"a80?; VRq81;0|Í}_8E9yguJD%J(yMyҭ!1U&~Er !GFF4ԩ,wkkk}5yUfΜ 7m''B iݎP:0)dvZ^RSEZjɼ}ƍAMgggAȠrVoc]1ʕZ^ CyH0 Lw@XrΣ̙,AaL[vy=M 4@nQeClҨQ#4i$>eU=T#%Gΐ 0y#rΝ;8=5M&GMҊӉjԨMBPD%J$&&L#Q̠Xnܹ m0*4}5{T:u*MF6\s-Zܹs6pXm{{{ՠGz,T1bDLZj%^t%KBnfQ q;{l uیiS40ycǎŤg =z4IЩt7mT;dT2!œv~@Q>5k&˖-I|[FAIq0ހ(nK/^XLbu%/#FQJ(Q$v6wܻvJcGv|,m\rׯC@ӡCˠI}U;vL^`רQRP1\qA;wd{ǎluA@Eӧ6zKExcccnq'NG PHHX`p˖-!UKq50@sզKP H t!at ~6pM(dנwr6-Z+n"G w_*?ER|z۷o7yK%J(QITTڵke.GA/_???Rz*,ՠV}5 0rȤ^ƺܶSj5gD^)}ҤIq… e;AkTܹs:w9IF'xq;A9s!ng% = NZj/f+V!\ӦK}}З(U~_*3h tbOBәA59(ɆwE SK\BBFׄ0((: ѺlKkԢ~`ݺuwɅ7))Ii+fGGG%J(Q\N:r/vnݺqG.Uի8pnݺTZU֊Y3gܽ{MF9yd0ӦM3rHWWWcƌٶm 㫝J,m;wnΜ9 F%qƜ9sa֬Y|?(C9oݺ… ܼyscggĜݩSӧOcϟ={vXΜ93x`LRW #!R //_ر#''&Lv]xqϞ=Z5G^@o>رcħ@Ϗٽ{J_G|5nΞ=S[^s·r&KIN̗۰wB>|8PwJj/_0Cq5km4_l_@E%Jܹsf Υ'IFq{t:uʊ &&b/7۷,4n~---(]vM~^KVVV]C}H[b%K,X+T٪U+Ab4lْc l0C] ۴i# lbf5,  -ZTe0ΩQ F]y2EXz.nB3 .)$$$ۗ3~*~2%刳lٲM4TqvY`^b1a0PbH@œh r 隯֬YZG'NH-v`+ֺukٴiSk5<_cfB .24Y#9@>qp;?JsjM!HWD%Jn޼ikk+ZԞ={vN٣%1k, ,#… #G]vrVڳgǏ,Ҿa0CڵSP̓˗hт51x`!^t޽cǎ M64Vu֭^~׮]Kse8(ta*`*ΖQdM\B/_.jժգG7;;;*zxСC*ǏW'_"Ap>rmnٲ6Poݺ%̜q(kzt-3}^Y*S`@" W'#H_%J _YaL2|)zȐ!ŋhCuC  zMM"RD%e}6'7+I;1IG7/rSmZr3إH]D&kȶ~x 6?+|8I8s e|OڈA?6ȸL^-?_NQUz2gΜdt! ӥKB hLFӧϱcǨ q<{]4oÇc6Ϙ1S޿!Ob͛7ixyyǏSs@J(Qۭ߿_=D/y !wϫWN:VZ2iɓzBv$}eMuqq n˗/_.q@vر^ݺu`}EYwyk׮u6k֬e˖.XB]v +QD\}J*Pq%a\_Kr,`o߾ݻٳgG… lxxwԉ+V8a&]{yyiLRHAݿ_6vqq9::dɒ践}̽$q;񈃃ldJw|IӦMf`;w㧟~Zjs%J(QD/_G+y2IyC߇׭[cשSe9rdsT o׭[w֬Y~~~3GŋB =0df̘!+  { $'&&fݘ!<{a-OA0@(Kin#w0𸺺3nܸOV^#< VVVcC Y{.)nWD%ipzҟ 7^xԩSnnn.]d)%J*n_~=Yv?2? f̘ҲRJÇ?s l"s͚5t 0̄))V֭[ 6iҤ[nJHHO{ˆ5jH" 5[l:rʙ3g٠W\ j뷳j9r XnRN 5d~.Uo]^=tMń :[l۱PB Pp3Eɼy.)nWD%_',K@%K2eʔ-[V~ȟj%nTMޯP WB0)S{ kEx̷RJV%5n+6c \yR|yw ֭U5u-J c 5\ruXfΜYBSSBxb.j8p5rH!p8|pV*V%7oߠAUK\%G =^D%Q6%K\gaa1o<<\r5''ׯ/ZBpssSJ5G7!!'l||<K$kCް6լvŸ^FaIy&_XJZ50Xq;Q3g0۰P~פ 1ٳ¸hK_ڣ{Q?U0RM(7{%J(nW$$$d˖-r*QD޽3eԸqcniiy=mW9P۵+jzn;\ubtvg.TΝ;4aJ(QDk 50 |5ͺK.C'''geeeI`W==b)1W;>o޼&ȑ#7oެvuQD%[n }ӦM-Z(^ϖ-%/^Ti3J=E֙IMO*QD%sn qyRK;>]|4~aeiirJWWWӧOk۷o/2^//;wN4N:Yf͜9U(QܮD%J(y.O`KNxxA2e$+@XYYʕK^i2Ο< lllVR%???J+y}0xta(4 )2,F. tHB_^pү3}}Ew8\orJrn #R/QD-[6O'/yGGSBy*j㨠iTfc"c"\>}ݓ$sOHCI#>)IQ!v57 _I/ {挏舸#~waѱAi|bbvzkׂHq04$87DF&%Eԙ31'OFzyI WƘ7Oqu r唄Zn`na\!i 11i+DNٳ8؄B*sT끉7!dhCgą :y y2""$))vgF<s|DE8ʍ , I|C|f_[4 I÷#2$Nibta2#q9سQGGebڢAc:[ѵ36 //uV^-+?v옟ܻw 2ԭ[moo۷ %anoݺ5uVDqo`u'g_Cнg֭qj]?Qopȍ+םu tcSJvZWYϧSJJwk+Oϫ.cʼVX*5'Ly.6߶6!6u[_ZGn(s5<0Ƞn[+[[Ybcj{mqYzޑZ3p?Bo<55[.|en>lRmϝ٬b-V1ݪ놲&Xv|AuPmKSo˕ng`'c~] ;76Pþ}AS%5mM|_OyliS޼ߗ(y6mzA!p֬WPG+o˖['Lz~Թs_լ]ɒnַ׮M5 %V-~ֲe c#jHpa{vv1_֮<3Ri螬eOvQ|ߖ)K߽Vko/Q,{wbf/}޼?YZl|fms:uzVwU~ ®~P*0 (n޼5j|UFj_MZ iݎ+렿~#XÇ~3'aT̷{xDѝmwտmvр&)_D/_3wBߊk̇Vѯ@Fgx-Cw|զMI&7^\2wpp̓'ρؘkɅ/趡Ѐ`?+=,jeY.7~eg]F}I.ym:oMWW_^=4>wpyֺK3جy6'eS< mZINLEU)%Fz9asܰ/<`Ą;[лLuI+CZ)E?=RwHh_rlTUy-,D vlBwMoűyek2f_ fά#ǣӃ"ϟ͚:dC4G_3eD^(yΝمB&s11?c?}IѢ?'?Q?\~N z^ NۄA>1 G^[BG8p{t\_ ҥr/t֬xDOMH-/GGfR.^(T@o7ߘ+Vb_ZL2 0DO g̘֭~VI2J5fIu;!6pk> X0uupHp-nAaQ $L2rrI{lD>'D/Hʇ47rGaj[:&v"B:{ά9pv fYSxW]yʱn-ARMQݚ)?pn󺓆v$SP 8ͽFk0NB;ܗfy/o 1:/ 0nE!-nPHwPm 7XfLZB_G&ˆzmyEeJx^uatqxu%ڬ)tayϻ[ksa%ґus.nhs GSg-[_P و㾪V9r|ެ٭ ΞJܻ~ @CB/Q6Y!Çj.\9uqc\\ 5kw& ѤAիwwŊh?ta߽({g*T9}}SLC"3U-YB8`̙!dlI\YSC=EAoԨcN';l{wTWzobǶMA25x!Ǐ7f 5m?~Zڵ ~0o>=;q׮5G!ҥ8s 1TSKMOrmDPz߾B!ʔ |zO>{I54<(Do1۷"˵kV4ΝhxHz4mЀKg On?{ĉ'Om۶m1bĺu3g,[o߾-ZhԨ۷ogv%n8]s[S=<,k:Д.V :1zU ߵ'gZ]XlxqhRYFP fnXLRsU\=qFk7N O]&X2p+eڀal~Yms\:K9*,JzH1]% f8le0kۮ)G}) [[c혇RN02`o7PIP[)LG5N> ܍}gK *wcK!+,iO"oJJE9! {7':>R6D܋{c„I153¢b i11A׮3ez zY9aY~0p`ݻB6> q$iQײ;؅ 3y_'X~(TȤFp0KIJLnYiPx3g~־};mAAOƍiTVAe2l"Otנ}] KO 2{I 1#Yab>0:bh/iEDG޺uҍPuG_֩$OhLJ?r`D4/Z47:>>є)/&gW=yB%62cj~=LP*0|]=jReq/,-׬M! +~įZP[lK!ܵy-#0ӴvN$Ԑth>]?k_T?xo^޽)gh*!ӐỻG')S]<ōy[$`9*kTTL B/5nU"޽?z}*-}:{$v 7ܢ VCǏDz ':Q;|8^Lǽ{ܤA3[&O*Hp0IرO'(XGosJصt=-~5}gf=2[Љ6c8}i{|$ [04g LJA+Gm_naPcv8!e Ѱb=p1^Rft7!l|t 4 U(r;tdE%@o8{WF\ +d7)(8{f]3#R|*Z.$98P_#C}֒dMs{IDm+`q.,oܢ檒a赓dۖm[ˆ2/*9Zʶ*㜫]YS,&y#{KF-4iʊ($Ƙ=Ops8 `A~'֕8JB?Ie™ ܮ? $TJOw?`1]zv8GA0gKu#W֌7fȼ'uwj{ sqCT7E#KbA;٘#.\ %ƿP LTK>z _U~kFp%Đ2|8L5kR{fS*Tի'j E4cX:gc lÙ3*fhZ0*̐өAaұ'N@(|0w__lCaxJ|8PMOs=j@6*ʕO¨u>}nUNʖ un,U7< [ժ}-ܮSy)5 XT SOsܾ ^ܷy |̘dnwuM?{$9ibb`Oj.V>cz14ES1$0FxΔɐÇ-H {gϏ֮T2S:"3+Is264$Mծ˵QN?0GwJ(nOM\<'ĸq}SZ)~ma;=2h!h 3/z  v0`dDVo㺒S.t6voza}ԨoL@'g֚ O%*P~9G.pʆjUin`?d!=`Ze/rN3Gݻ> [7y8IItb@y6 C <AAK8c[[|دboG+ΧWT]M~[߭\y7'Q3d}~XTFy22^66_ovu͛oѯǢ`lccLDZ|"Bǎtɖ-I~`ee ӡ3|.]>)[[*$$~`h#̩[ߧz9HG4xaÞ#.DR" رOO!چ}JG ScPԠ1QUBJ(n%y2`޻ӛW{)H;=}'y8 CV8n;N#`LR5zl,|K1SNSaF-g[0X(h09 Ѹ>(qƗCMt)r#$]իY#S9 ڮ+W Bq/atܸ'gW͹a>-[~oNƍThb*U;b7t%tGs">0o_ÇZO8=| G$00ynj'O}?g 2jAq%_vC^;U(7FMjJ L; FiK篝X.#8a$0/h>#qy.up#XzΑRK54 OK]{rf1y]w?t:T[I<6a_g|8vO{ HO)r;$SnQI}cXCN86C96zNN-w}1]<L3TY̹]b.N *4ۓr#^/GF&m* 'NL!'4kx3[6wߕo QQ!96'$Hו+k`g-[6|SOlz E;=Q:u ʫV5AcDZ 㺹};faE[ SEFk:kk_JHB/5fa~ %ï]AdhgUvKC/ $ {eĐO+- U|-Ӥip;\'PھvVZ/̭5 hZW3Dz\،NX[ńA\h_ROf)V{(Oy;wv ݳ0dkIKR #2,3S&2YOC>%1_aݺ},S&O+!nn$c$n_Saw]O۶Yϩfib~|!5, 6&ih\;ч،m]nOJo@2mIkxK~ZpI@q(9@{8Enݞ9(8S`jb\z=Ӧ=cmw|,^}>Oy2zm WҞo'k\w^9W_3ezȐd AY5{l^ Xk|[X<:5ԩXghن ӂIIY2ddiByR鈋4 qcCŋVS̐Wc\]i.PNQgWcm-# i#KĂ\'NĜz{YQD9Szvco,-;?̰ᡀ S>.A`,\J~{ NRajf:8τ}ZSv\}cy mM=%w6W>v^?k$<ȇa2fn7p? w}4|ݻX~Cr ?xiVC!}%Cj4fUYE]f ˘gBF' T0?P`e2{OJ| omp>Уeܱc0+Ν bp;aEMFW)/Q8X|>==3>(ݿߥ'p;(uP|?SN#+rZ[pPGA$S_eyTq䟁U:ecϞDVLSnҙ30j,\|]5Vf۷mKdcG Jrol A'Ge=o^3+VW'O~ܨR;vppp` ZQۣ 5AߏL k fs Lsv`s_4ೡ!_%Un MHD*QxNmiIw<s¾N}Th饩ěq{T}p;$qչ܌nYfs?|W (.&.™}H'#pGjŸ)Zm7d2XW>lr8tgS֓~NƕMӰ}h(rְl&zs $'EnKL}wDGsh8@w`KL|u&&~Uo/<#i|oܞkrܕ/A3drє)& Ӹp!ƈwu5!ؠ8=GG[ɴw) k$bj|lĜokOאa n'>sRfW͛:8-/NBw.5lDOڶ ٸpWŊ˒2..ɷo_dO>{'e#QJ``1߲ewZȵHxh+rC0 h8ѠAywvmZ?, /6ܒrs2aΜ/G!e86ݫ%/ EIIRv-OGX6cD-aYʺ=fB%?6I\.`N~_LWT ?/"9*ON,O%RjlG%7"+![\ . ? :=@{$P#yY$On" dY~l)Y_qԩSsΝ5k֒%K֩Snݺ˗ }zIk wXSbɑjbf=}0,TK{9÷4mwP+֗12jɁ l.>ȹɥ]}-M'7ۮ.2=.;Mw=ҵPpN6hiNu7[[C~.~[8/,#{g}%ˎrd@4vw6X);w\[1PwO,j8חMZSv-F_9C@5ŰO1HqnDDUvSϿK6,-Cwo޼?I~ln^Xϝ$͙`͝%Jliw,?k֓=dK&\сPV[ ]A g KSPΔ_[]{i[h=;`u͟kØe޿SڗRm=)Itw)o^IZs̡[~,UJ" I}ֱ],ZxĈjԠ3KL$`߷O&'WM>:'O@)p;dxSB-Z|5u%Ki{ MX &iKޑ6(ݠ72t*@$~d\mGѡC?Zܻ7A8N}@'O~شz…(Akz@҇V6A Tׁl+٪ׯt6@t˖djoOu{?:D hǻv%BŲ[<ر6j5$pR9qo+{ʢݣ!Yheеk2޲嗃wjZNK0͛olxcӧ?1x]sOѣ?dJ2Dt8q"kηi s+e ,me, .Q3gԩŋn:~ӧOv%DE\ؚ.!BSKхW/}z.lFz,,,\Wj|tO+1e%"Ma +J&EpvuQ'tg2Xkhܮ n]6u귩ߞZEx4 Dzj͵ Yd7(cn>.21.ÿSvvkd̟hBŠIV+~~9+KrͷuH j>Z j̚%b0eIB0 $_?]jUui>i( AY:SO˧?NInnlt7ѥֽI/v2]V2?bC1Gz|5QzwY0GounΚۥ#߮ApyW+b  nP n?^vP6/̈42E?=**Tz'z4B]H[?>*`C}M̿O0-e V_YXa]6 QRw^^}ɧ;Ijny$;aJh +:8*KAtcϞODAnh_i7C|A`H&St1"N׮H)P (n !ۑ'Oyzxx7?%߮uvYd$pf/\譭y`q*봶t f=b]= ׁ mpk f.'v^WcUJA2']`NCb\o:}o}CE =:iG;|]zu-$6㷵w<> zL!RK;߆6gS _qh Fhe{/i)jig4dU٫Ǻ8`ۛO,rjHזk23ԩ+mkG+! e<&jMRk&6vTS+F1(4Tv-#Lymi5Щ"1~׮lS#EZOn=6~[ ;j}b7I1^GyflApq?qӼ*] X 8#O7eQsGQce3ʼ@x([38!!qժ)Uh)Ѯ;Ɯ8aUPhtӿjĉbRmݺ?-c07 >0A[z%."E^+]H`:w߾h&anQ LE?{6n<1"%pI_Z3;; &i!tfpcmɰS~ݰ! Tʕie S ]͚zIn8oN\l?a#`(Fnj…xmzo3?K#Gbێ??y2FC:}M{`=+i9- e/׮)\3>"%9g׮D8(6iC+~ߪ՗˗kgDwy۶_o=w~?UX^Fї0ߠqiiߎ*..x}?Rz7Dͻ׹-Z|ūVm,9}c/pEvq#j͛o?ķ~56nʕ{إ ]7h 7-=3f<+W&R%VGnO꥓o7t2Exȿ'?Sy޺.*JJ NNӌG!Tm>X_Ó ('O:v,pam<7ЍtuFeN.RJ͍!;\7$ŬK'HlXt8]a< 60gwJ4?4}N̏Ϟl$OEX_W.qqȗ1һH˗+U)SŋoŊG SZ +y- ֔x{);Xlr,}|(QD [7eоҦEhIAEl(=4|gj8SKwk*…k/ء:ОY9r,XZj#FVR(n=n͖\t9Z7y]7QD%zlpDJߝZ^vA\/\MLfZVǏ%_"  r?iw&nvݓ3\a=@o'3rQ^cK[̽99qի۷o+W̙3o(Qmw-1E&X\LUzZ:گ࿅3U3(Q+]V;y3HR[x^j]աCL2UPO19p)ECCC}}}EK&||\r%00PL6  2,(-ܡ&E˹Y ',KcV3;9-7;4;cXE3 g,zƬfvߌ1LTD_cm)'N̜9sҥL5AUYVZ һw'O[v툈  H"EOچ +֦MD rʆ'%JprrBޞ"Æ |Ϟ=J2/_̙3IIIݺu+ZEP/fz؆ ,Y;aPBcn:!!RJJvڙȠA(~>իG`2׈sEmoFΝq… *T0̎;P5j;v,nnn\7UX :tڪUP5kEju֍҅|;|0j/h0B sZ*ZݲeKf92>LУWPv ϟKxx84i)SZj)hW5Dq .)E6mz]OOܹs/^ʔ)$Yd{ݻ7o޼ٳרQ@)hVYt)j}ɚ5k9siV˛7#G [l'NDV˖-˓9sf1͛7v\+|YY"߶m΀ǴBf5|=z4jӦMÙ:uܹs7Qҥ Ew9X. gbhN{P>|8Ej5'ʕ5ܢD1gsn"&Th& ^xQ3+nU%$$|m۶gΜ÷5ӧOWJYuiժly|l\\\ aÆAڋ-@#E18;/_ڀrre˖>/uԅ phѢDZMenݺ%''Ou\|q}S+tpCG QCNS_LfmĈ(nW:wHɓ'ϟ?\hyɒ% 2rȃvٱcrk<֬6n8H5OOQFڵ˗/O8AڴiǞ8qsrnV7oumKN0;asLf 9sヒW~zܾ};}}}/^YZpB|Q 3f̦M8i>}Y g|||xiٳg/^Ϙ1d^ڸq#>Lg97Psssgs0B0Yfqwfz"""^On6!:^sN>߽{wʗzҫ%_RMLH}G>ibRvN4IBBBS#jGEEYAeI5HpݬFER 2nV3;cMߓjF31eX$f3e~fb'#İi5F3-nzBޱw;,WX:ӆ*OFqk0#|>T˶+QܮDWXb Шhh+nWDv%J(QDv%J(nWD%J+QDq%_V+vp[ճDv%J(nW@{Xj;VSD?^dTY캾ū!& EuJdD"bx7x:^:ۻqDXz•(Qך5kfΜsr@,ϟ߳gGp~Q+Jdٟۛ)o:^l力ە(QGB;=bĈ%JdӅ 4lG!<<|ϗ/_)X`ʕ'NHq{%J+Q \3H1p\+QCȑYf.] E*Th!!!ϔ)^U*U/^§(nWcÓ#.^B#uNGL?V˔+Q:9sDYf2dHhhh.Sem:uR./^3gLq!w%ە( ʥe˼'M:?y:^zeݺj_KBBBmۖ;w|yzzEһиۍuNrEٽ{iJ+Q͛ZpjxuUOko%'Px(ٳ'9r5kVTTTDDDhhu֍ G-66vΜ9ٳg/VӧxJIÕ+;Nچǫ[kSL/D;kזf͚7nÆ gΜw˗/?eʔI&իhѢ666}7*QܮD_PNw~Q#ubW*] }:G sE۷WR%[ly̙!ŋo~߾}%KԲ¿7nܹs*IFv%JWV϶זg%;yvA .{ZP]ty=RJ 0.:t(Yd9@w:\(y==<<<999^`޸qJ(Q7vxs+֭||vu  >mmmRs~{\\99ydאy#ԪU0ϓde}V^}111[I%JV.CׯGW۫W/@lٲ'DDD4i҄Z:*yݸ=>>~ҤIYf-Zhhha˻ɓ;vx *dΜWJJ(Q7\SPǏktҗ/_NۃurEە2΃m=N'O !GEEmܸ&o޼ǏOYMZq%J(Q ͷ#m HwZvu7o^|d2j(J^ nݻy3GGG@w͛7/''':(NA60,з"vd봟 =;Xl{~Eh^4aD] (pŋܹCCp=ƚ*QD%kf͚;w eʔaK^7ӫ5jԨKٲesW3^^^j^W/ޱc-[&qnPW^]n]-Z|}$.vrr=z ̳gϮ]vy 4 >w\׮]ya7nL-mև9RJZ*:ex~'0ߴirʕLb2PbE4޷o_???D*U-[6>z3fܻwO[ncǎ(˧JϞ=>=D%Jvoo+V 5+W.͛'3|G5 _ti;;Ǐ[(Qr+W萕Ny#<|,c77,;wnY7#Hօry78qB֢x g>uT'%%θ>{n+`/XeP L;w./,T_|ҤIrXpa) GFoܸQNtpz3Ç] |G|DEdYVSJ(QDqCwxٳ6m5k_''˗/_7}||mxW)OGk̘14$E0X癿yFGDDxzz(PdZ*暑_SN0_"E ӧS+`[ji1-X_ؾr[dbժUC*al=Gذaönݺ|r vܠ۴isFZ'@TTyzbC۷oС#G2ePE7nxiK0R^=O֭[S۷!(QDwY [o%_`lhp-_7 c7nxp5'''wFX866>ŢE[oſ-ZxҥK^yR\BqJGypG@wb` 9sБv>|{bfn'(&~|e>)>_@?QUW&yNїT|v> +P:H_=ڜD%J+nWDq  B[nicp4gΜb t5P۷ou͚5->p@-;wLq8ϝ;+PˋtQĈt:t(ŋz=j#OѣcpC.R]6mPE̞=Aʕ+K…Q(S mO=ڶmk .b{eԨD%J+nWDq 0 J: jooa]T6,3|8'Oz…@0}jy22ܢE 3{xx0RX1J :2+PzժUΝ;F7nŋիҳgOB^`͏2D ?H.=f\r]vuss;q%KJ(QDq%J\n%efΜY^ d˖ L+Ufî0g\\So l 4Egƌ2%s߾}~{!XG[n)g.] ú{SZ5Ν;2{q䷷k.  U>##둑`{5kfowtt+QDZv,"KL#X`YY,Q hq;% a]vݾ}ld=nkooOY|…2qYSFsb_8x **T1I/j]tAM]e5ցu^hӧO߾}8SנA=z4x` Ч޻w\jp;o]v؜9sBY커]%J(n=o+V8r5˒2|MO2!CL: .uiӦ qtt6NX+QD+έ_@|/7ibvx" q̙/kP\R: $sAtǍG))¿Rn?i ի7zW{)Y.w5` }Y_<h˲aqqq60y2e*<<ز*;oVy!˗^L O8M߿?k|̻CzbPBQf,Y;wng˖`Arɒ%7m$ {ey ey8 :#۵k5ja~_(QDvB`Yqhz 0I׮]e[[1cƀvvv9w^$E`0:ݻwaZwss @8|pYh֬YӦM!Nog,thɓ KK'A&x+2R5}{тpNW[s{7?ۯ닖g~Q#28?|`C#F8|@jvblݺUe˖a f8z1SNO Bciu.ۡ;wWǎKh,'yuذa@]/6&MUj$k۶-ѷUR.r[֫W:u_NzUHo+W^.5?yݻ{n+Ç-~ PD%J܎ڥS` ̒%˨Q 1?+VU.ǎ˿PleL^V:t [蘹ԅ{o(.#Ph%֬Ys=@&tA5d+8q"iMZZV-///<2dɓ-O#|0 06^j L666;# j$hRl1ٯ_?܀)KC)qjjݺP Xc(iqww/[,doadܹGi`'6AXRI߸A Hb4hC-[JfTŊ鷼yb)\EAzFHx7Б9͛7RΜ9g#W M(QD2<ݺu'Jl@TZz`ǎPSmڴ1]Fdw!IY@VZv$ֵkӧܹ3001ۋ+K=z@kT 6=>xL]|y:ڵ H+M4'qHπL{лwoAb3nz D2e`r^r%P?~<-X ` ,:\3g$1c,ܱzjt(uV@,"c@[mIDHzpEǏ LEԀKgϞd@-cϞ=|yٵ"rtF?<n77LJk׮ WcY;E3䍶yf 6|l2k~TGoߧJo<1iڴihvk-̍}Z15=y2%J(Qs;Eԩf{Anm.)厎r*Z Łץj;nݺ6m2`ĉ@85с$k׮f1ɖ"U(7-+ƌ3dȐT4s;EK.wb 4 p>JGEFFԈlloOg6?hd{C)ۉGq#VZ,*p2uT.111gϖ<%@W%_  .Hv1`f͚Ed~SN\RM9sڵQ-Z4$ vETb,͡ rlٲn:v%J(Qv@m ^6g@%<_D;NN ={SͰaÌq"n1&LD7x^N:AD7%WE'W\"Bs΅Zpxz0`lcQ\ No=rWsyv.vܙ|X!s!ܫV|ddhRRЭ[#(11+iݣPbcC9a r8~jXNHfDW'7o)>iR"='XSfJڊl"E^z;vL?jժ6eZt1FGG -[7U]cޠA7n؀ocvpc|ɒ%2Knԩ2/5~ZmY ],">}tPȏiu}n<9r$^\Y҇ٞn?vNEoooŋxF#\i$+vYƿ\ߵ!عzCSsTǾk QA;lk!. Gmms[z=tñzzъ'O"QK?w$ľ>86v[. oi~{9 XIDXs1B?=oM9x=Mu.'Ni7̥P;l:0$4Xwfz ڜukp7j 9oG%O7aoZrذvL;koɕMn8cwHDf `.]>kknݞ88܁oE^}q 34-7kFÿnI;r[: |ƌurAA(l֬үk>>)HNN)q"4GH<(nHrtHc[w}WV ?:ԵkץK ] !'N-XntׯoʿիW}t9}ĉَ=Z&Bݶ 6uZ~ㆋ ʕkΜ9Nϝ;'Y@mڴfѢEd=Ty]QZ*ƍ!T* Bm۶Ij@ n;::rIH%kTX;ձcGF~۹& 9s\zuAn۽{wFK+e筹>4 lbF``;~)n8ЩNlDhgK8X!ƍu^Wzk ppH z]חu?[4#jZjСf.˳BtԉE2xYhI&;ƍ+WҍUlYtx+VԕnN֭uSLKi Sdy (n:ͧ!&L'ʚe -d (WhR-?':UtžOo.S>~siFٴED'",z\Ú@}#"b|T{_z49v-8vUapq͑TJ!ҊS+ԕCX=7Vnn}yb 4pߦZ tX%vڮc붾|e6"Ruk/cG&VoIkXE.Tc >۬*<ԥ]=wt{ `Y#|b(cLc=EkTV+ [FExkuk`ZNmWE> 9}D[h97,sb6T]b% oڵWO[={>IN2nY\\ԩg@Ynk"rT}|Δ`-i̛wO^o}ֽ @J0c>k.3m \x|hrϞn%KҥgJ1E_Ѥ׆ ~ 8KDGFjckc݄jCW KH-,'&Xbs-DS<NvtORlZH`ld^XxQ*z޾6xNc-^: jqRg&- F-ZCNѐxZqߵh?h幎]ڗsǸ>y!'K~o}g&nC.*M#H R) Tt#+a%瓢ok/EkE\o遉FKC\qmI2?{vp103"K2v \I)WwuZ m=d<'l d[QF?,?vS+ZGIuiY?6Q;7IIA7o9I#\Y;t)%Sbs?xֵgAG)lߞt5 `]?{E|,pC>lu\mVOox%v[_~yftGm@Ȱ\×scծ˝ ]IV]mUƿdno\lHǵ%eݛzC2^!li =:j5$oe7X,;-W%<s?H{5n_ §$m0,:Nb-hoHhHphpՇMXwtM+fʫL+h{J*u9GH:#zBӘ+ ,90g@s)&˱ z00gP" t;xaGy'xDQ Fv-B @f˖݁`9d&C0/ܜ7F %P UbXQ:u5r{'FExl0yJDyߐ[ UmO&4a9v4f9=Hxuiі-ɵk;g*W]KR|ګ_C?*VȂsi>]qu㡳 *e$zWOH)Ǎ\$7 MN"Ո.]>{#77;u-^|W~C#Z._%iH?^}k͚[TMH;~N8C<+T~»]τK9s]Ǐl%_7+ϲ%PZ#Yɯ--.Wa:}˖-ƴPFctE,AFo}/ʈK?ۡvMH6 i " Q7쮍aMkKPnZV.6tB +>lKxz>sv BwH7o61/> QgcDX-zLu<C͗b\8Cw(|[Yp۴p;\īvkX9 2(ui:6Vm`PRBq 1x BK,@BH5?q8|bQFf#Q7\o~;)vuqxNܩ/ B I\NLw޷yfǸc]?y>Eb:H 7qlUJ"PdmO'p Ν O3Q ={~ C ~-r;qRQٺ~?nvޝ*=!˖A ?(( DGzg nuޕ+o\ǀXJ׮x̫@iɒTɏ xr'>˗=o',$rڧ`y%T! p?z"^~RUe/Rb ICIu6O.Dr j28/Qk֯EՒOKG'cS/=&B\\&dLQJ&>Ko5~}#w<62!$4x2{q-=x, ⅵGf9RS#oHg{;lMȹzsS܎QN'@ॡ.\5odMۡ2 J a^N+]ZC> AV@ݮ]fv qK3#'n%8%@0r[@#y{7V}:B`9 <9r$v'O4A-dpG[Y6iOd芀pPm 5nْ,2,wHdäTHup,r u{E<>1x>w5kXIUaOn)qݦt/HO@o(p}֬$"'~Mq2vGOH}jnJ }9 mP[}EoaJ+QK׷qƕ*UڳgϯNPWv<}0깫'|z" IaNlHs/ߣ6 |)2/`C).Hu`d 8585@1"|}{oS@ocW,:{ % 7C$=nl>/ya2/uXb䍧ϔʹ H"UCRoh/]sCk>6"fkFT%D x>ý,#C>ct]Nx|>l^]'b£x4[2̂hPF-LyfOiբr㻬+Dr;$U>^k+U𖿝;&L.y566:;'C_!!a25s5_)eXbhJ)g ~n++:GVkr-ɈV,*ׂ90ƒ$cD@w> mP#2z^*>̙s_k/%SԏVӢR4TT=J8S)1{ٳw/XnFagr1p,޽ 9rwѢBşI{1t&ק޴v&d$d>qr?Zn8e"֭,VLˢs(nWW'̨NxU u>F^&캮l3>Ht鸶`뫋ӫ;h ^1!*džJQDOl--K{o\_a!<,ÂͿ caǙD ;;XCbו)%|ہjZN\ޟj)ME^Ն87\\=}٫'El]DTYͽ0B%ѥڒTAEs J'OW铺O[i엑ɓyoRiJ,@VDpao  Q;? 3/0YL 'K÷&<9rޠaïAbTѣmEh1kdtcs;Mfe?iv)x$[OZ&&` qpC@SY=-n]4E |GB|H3xxʖA*UOɫiDW׮] שSWs;8gS c ^Fɂ N{k`|Rm eϸx{xsM2n`ӫ##+L3($zqVY>E#][`*)iԻkSԸ=Kx'tޒ{KDX}A'ncvhTqZ[1zcfڶD=&B{ i'4s|؋>U6 g곩FM a,\%SY/Fb"@%'!wEooSiNP׳ ?QÐ͒UaZRv[dIm#kZ&4wvuxo[3 c֭"j[Ȩ85/h4(&>2aM~sg2h}}?˖Iun]-S]nhƛ!h*\<~<<~fb?v5MUu(ߦ_[nzjJ.}e5:Dk+W_ '!s6')'q=p7(c`t^SnӦs:dJ%K%kXsیaIJ&{. F{*ikG6,U2  p{|TQ*xpN pLE>/Uo#-8gPoǍ 5]mJvUEpu U#:uix;1F3R{uedk!stb# Xk>WIҫSwsn}yAߴցƸr[:}65,r(QO_vsEv"p/܂ Gk;΅ƹAsǘoo<$6,\ {|\7x2=hYnIFa3zݹsK32wO{x"G^6l UNSvb(;|}9̼vL4*Ξ)X?̷slْCsf_RLqV҂i5i)Tm[IJн$K*{_ݿh@盹#,e#9T@!G8F8Dq6!QQTPp@0*"" xr ␩;߽}~}sݷ5h^_U}U{}Ԯj,",1j"{[pʕߋD  (Eqִ0VDYk1c̙S3QQpD߀jpqDD4iȇ97o {2En]_GiVZ9881mTy҈|bAPJ޶m,-- L@ki /o #?b|}mp)~;mGg+& ] >zm{aj2,HD`Wݰ/74T?1hW z~m@#DpYZ Ɠ)&v x6WK#͈S\9Yssܞv2^]=H {>sLT=|_?k7F'ޙliNj,Un)LzQ).NDCrCw%B1/ߑlo0&1$W'#;xNIiL_ 3 o U㠄Tb''Cca<6#aO?}4)9;VAZ cDMdG8pvݥM!O]UلAAU6qU O&Fp.Pԋz ^Q9g&NWu[`<5k #u^oӦM˖-ż6iQǎ۷oVs@/B.ҥ|}}-,,<==ܹn`i G'`l @ 3r.H߷ TO P\Pz#{M2@W//G_zځS;0aF6=+,q(:hs%l4@Lt_1~էRu$s|Io'IhF =xK6n9XG'BXkpkȲc!*1~0kF&,I5}ۿLIAÊR8PѐoEO <8M莾(xDYavujbP =#jr8v\\'D{Ljq)U nPM1:Cn Ss; o- S eQaD/>$ȟcǐ u7 odx8hiq{Рkv1H&FAMB)T q oW#&-j.^FD-?tvG?Cpw-.=ߠGx}뭟[|I;-`bෘdvVޥ_֭"՞=4MAԝh^Z "oy1C+||~t׮Q%r~MC= 8io dA*yD8+=z ōe`1$Oڹs翐"ڐvRw,9yHNCۨ!98(b:]sEӉ~2xEdgg%*g\"Qe*++;vhggw)9^3*77oѢM-??֍Tjܸ$K\q~:9n~P%_5_WpIfv+8jK|`y47ݚv6cR.>_ fnLhn{E B>Ȥs av[bEk't 7|夳aDe'PoPʲ)[1%䓜ysQ"Fc7L +z;hat"wE:氹 vQqjثْBO^0Nڶ'~03rT|$z-M¤}.Kk N)LgC}9q8ӌkER%| K2~t,| pʝo<ƷE~{SU~a[/XڌU_7n9iF$+Zx goRƚN9sfrkɒx ځR`؂,tՃ+9\Z$z#{zL! Ab 'ˍ3:4]Zl$sPc·ի7b 8d^'㏿,xȑJ8nkkD;lؿQkBݹ2֭a*H斖`2ԧ)hR|;q⿎aҧϟERXP -Slʼn ipES:> &'~8K}DRlḓDžZ\O)$_'Nц d. pw7Oى ikרe~ p ާNkZVuu۷~|x:^W?Q͛7 U{AA x {{4СQlg7n,..w8QcqiWaŒT8Wx6a{393!%3ah*'7L̈#+BnCo6&G梂R $#_ŦFZ{1f$"7puJM\S<-|]bTe| xg|KJV*wKV*")ŤDJTC)ȍqP@DYx2;'-`$E-̹TKBSx,?>* 9(xԽfPG 5+-9Y~ZZX{iy^DY|`@=!4ZO0m8l ?Kn&6-K'7'~pm}\_߸;A`7o鳳8 PV 5K^5F ڲd{xENNk jUfM&}iɁ|j0iQXX)ߴ={ +*i sbb>- E+ǎUl -3vUo3EBFV0'|jn cη+E;ѥQ|FJ81;QG<'Qt ?U8u=p_~i87=INY/,UH^ ZF9R $?tȐ!m.bCӏfپ'C}^ŋyKyccc۴ickkۥK33s窛NvWWגusCR}~p"XsIBW`Q Lw-D*&*ehBZWckK*g%y|Gj!z;ۣ\"? (f}U &sCC)y>㡔zUU\\i& wwwN'Y[nN=DuYEÇ+MJ}YYYjիSxs*N潮a yZ7)]LJDKIIOsHu;7o_ນalw2ԣRw;;:>^vzdzݨQxϷswܩ|z=[D_}nbӪU*+{8PIqq1HdVE'ؗ?ȥqF#J/--U~mL!aFj3?WvjHBBjgj⭉jgdEŒ"j&q DTQI-jVS> v)m@@Oohs ؖق_9N㤤tؑgӦM}F! 6x\~~~lOJJ4i… iFuJhǎ .;vݺu$̜1c1z*fP$ٳgf@fӦMKOOg͛ڹsHri6[`x3K,!xvZ-gfΜ YF?IHHr2!Kzd>|u88K8{$IKKa9P)xP̓EfC'Sţ]vvvv.}eԨQJ3 4ir dDr؞<君R/Lbb" p f%.:"ϛp5kLuւ;v+ n8q vEv.gUKp{hh펎rƍqwނۛ5k6~I nf8 nۗ.]*NpI5At@=99Q{a?nwpp0vNFEE R$KIII=+W4m>22RvVK$t2747ߥJLJSG R.]/g)4v@3 kQkk_~***<==6lᑛ+QGM/^ԩSv킂j6[`-[z{{͛7PoժUGz뭷zdɒ[nnHR/&쵱 P^J#ݾ-1dI)Zq_gKtyt_|!r~ڵ'uBNHH`8~\lIv/q>\ ooG WjxƄ9ᗔViisTT-cuPw#Ւ (RDe{{z:l=v)Wk>T^RRRDeeeÆ 4N)z=44zg{U^~މqryϨ^ L~2RRR譎Nlaa ?RR[7zinn)˫ݖ.%%ǏoѢG}$;KIGhOII:thÆ {gIIIIIIz*))7nzjRR SSSGinnrMeq))))))WOeee-|\]Jׯ@hwttT͑zzƌF0#%kV^^ޥK۰aCgg爈9Z۶mkٲСC###e)_NNN@{nbccv<2"_.ؒ_HIopttOs$%y͞8sL;;;KKK7770@޾^%~qlNљJ-3oH0{#%%UOSSS]]]CCCu:V$%eץKss͛/]Tί$_wy-w.&u˫84骜JJ~ (++[~CÆ [l9uԋ/=77WⓔԋݻwРADӖ} :+%JnU$99y„ ͚5[jyAF/ǜg6mԿ[[[ssv˾1R/Kyy73zM&Wk>6^/+**"""Fݼysr֭ۜ9s>WjRRRH\>999b=::oܸqE$w'R _EWRۻxOY{?Wʪ6,Jخ/ϯηzcnK6~[zEdU̘s>j.*dqn!W͌;zCΕ$7ե('*|bªǥ|2_VVܻHkb&&A O6&Έ)fjg0Sr6_qFTS$)((0)KJ$j@_>{lgggkkkĐ!CظiӦCCqqq RRRϬ3gZjɽ{~7͛4iҧO 6X'BU=CCCN:&L3;99ܷoߤI&D` ,]hhܼyچ#G0F~zA})fӧOivzZZܹsf8ĮӧY=c K,j8uVv`2eٺuċ+VPPzz:fv2Ν;ٞ nF"jɓAeJAhwK d֭[sQHAJk~eZI?Rwי3g޸q`\uR;1,aiPPguN6mjii WXXXoVߒ9jݺu-5jdn+p{߾}/^sXԯM<.\5@%'''\7߿?rH333Y޽ 7k֌=33[nj $?p5Oފ hڌg5k>>8^:pNJJJJJJJ{e"'l"~Ėz쮖 endstream endobj 48 0 obj << /Type /XObject /Subtype /Image /Width 1000 /Height 167 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 26091 /Filter /FlateDecode >> stream x읇[{ΤzPTPd7ޫ.,AJP @BRM xޫWu@̞-sp 'p@ _X%鿯s-qZp;`ejDZpkRxRoS"[!ܓ%:G,Pk9H<6_8 ץ tߤffrqpKNe^! s_6c3A@'| r;9Ҏu y\{yRpz0/y,hag_m61f"+!~oien:| K>XZ XX 4gIdh5rCSƂ˔+[?A`n%"\vEd,29l}M[)ӄ70!IxGGNI˧!|Ѿ_:eɩ:ׯzyq/jňƠ+_rZ#Pwmͽd|axn0+Ӟ<2|t!iUڒ$ FdN*})|˵w"V,4*wL^=j_&RRW|U;8U^WBKcB>X~e]Q'ZSК\zHn }R[jgj r֬򱆇$Dr2OCSeM>_9!QV44={j>ffԇ5՘<9H}U~c.<<{[ 9*X6Zv&!%OBQ)Bv4Wp>:a+pz13뫯-+Oseur݌Qx8Y)x  Z^bP1_cA^U'Y6dO7`8 %2 ÝKg9f \ިړ-h 4pg-po19Kcm'u)/&Vϧ[-3 EG5ds ro1V$Fӓ)"G"pu`;K5\xmw 1J9 R#$>G19sk)nݢwPr0۽#jC9L˚ "F̼4ţ+< C aLU^&>ƒ?[Z=B/KK3:zCzsseHLP8xTR9["Ͳ9̜9b{{?&{8g] ;iz#n"ݮȠDZv/5`.-V6?wb8O9ط}606lk8.)wq.z8l?t#2? mWۆ,F2ih\{܀5"sA e9OO5Q2k;+? ܱ>tgmR2ֆSg 4:bJ>q1w}6 zS8wy0w^(3HZ>P9h 0YQD/Zxj? Z_ϹD,mĽn>J0N3fE葜1Jn1&xi!{8Uzl+ ås9-idh;:@xpȡ|2 5yϻ틜7x/8zi˗Bp Ρ]docUO:),m <xqhxpZp޷dzm %\ |>Ns_놏rxw%z/sc5asf2 5ywbǑfcI8)on; vS=ڛbC8K|f$w- |l0sWe .]^1BGo}@4n:fWùӇ;Ղgj96f g;y=n o0fsr&"&Wf3asyh|}lj~by/Wn`E9~¬WBM\t|"Yϖ;jF&FZn{䉃1Z_(DP{c0 Kf%ΫVCG& ֱc6fl[N僵Շx#Gs]yQ彲ɡʝV\{Fퟏ`s}š"T{[3ՓᇽZv TGmݽ=MC`{ m/\>\嵌U(fY5l/trfV=W9VV?˦7l0U ޮ"" rZw7!{ t\Vd˽V_(^k%wQ21ҟȑ'ZB"8I$ qذ?6 ܙ'5twځ(13.>t.x8mW8uܡA8yH\&M8r0mJSV"Cvf5lO9mhZ_ хq^hS)2RבVeft 5ykl٤ZY~?Tf%cŝPcOT }A"JL yH=v]ypG…K4DWyxq.hE䗬-K[fpTJ%" B+ʮOJR$LM+gRpIڤ!+9]&}KtYDw; hGmI&z$CgόHȇyR:-R!$3RrPY*"A.ɯXK'gs)dz$a$ J*%xCdd^y)%FHkMm9?`JbzL{T9kꇴRbY=lL\[vKuwIc" $Gw\$g&Ҁ:_Ba FeU%Qe'I{xer7>?,yD@=&}у=% ZXs2]xN=CRֽُܺc>gsNEP hKB>wIdx62P숲~ڳ"j(䤶OrB@">p#six뀶>Բ`1UWˀ Il|R/[3S/u9Jo('v!ʩ>PJQN\=p<ĥEVvofȉps!ě TC/0&`|QS'ȦcC;5c]j${c[D#S='-99!~{.]}<*0iˋlk;g+keU0*r-bjbE?{.\(_W=P;VGe4/ز&ӫ0!xP 2tnx`+nCIGx9 Ϗ$\ yڷ':?H0 cr_bce򣌟Hf0i j?,( '5Q=X3s o /H&?Q\X#y1^#Tm@ xaW`eOG*pXA|Fg x2vp3$miх]n߹{@YnHЩGӉ\208 'q^яڋ_,8EC w|Tsy[ҒsSW+3chxzЫ{Np9ӣfKNO2o99'86+穓;;+mZ< g8/chy܌w8k. !ϜZs%NA4z眰"zU,#Tlς;ۨ[xBL<,Ծp[Vn2c -_5;F/1[TWd891r^8SP|B_ԝem[DQ cێ|K |6@,[ Cb@h+ ^6ĕoyh&ߜݱFv;X9AqɢsN=,=fhKKxŹyriUxP5TVYau 4hʗC6I[7f><|vq.rҹQW Ĺ%sM㞨._bωB:;<'-)uK89Vη:rlY X{jQ[8G#g}y^#"0+Js-SۅnZ:߫'Pziu;8M /I݌b 9g{f0T 0=ļ =y;ΟNS/YMN GL?O3gHsN89q' 9%GqsOGkm[$oICvI.㜱Ŀz7ߪ҇[CcvIh(?%Rh@/b^"0_':B\f|iVo:JЉJ-qςY<ߜNOˤ]n–p}EJhCBe x?H ϱ8{|tKW2GYrbVQ9sڵWѿ*+AVߛ~sIr2R[P1+6]Â\c[,yzt51]WHוD:?yqbX~wcݪڊq@Coߕ\0 '>5K17!h&;\U޾{%dܪ-̞*k_J\fPuܹ^'\xg/>;ܝBW{s!_suX "rwEr8~>u>;TP_twޘ>H?~mH fw{o>PV7c##:(ܭpfvTfnrk###jh ukEuL3e)-"n% $mVW*#ЎՖ7ʦτ SAFg𐦳| F"pVF;Z[^;_2vk{˛'U`nn\Zm]`Mfm]{T|MDDǕY>TIZR;VWnP~=S[SHeWHu=/3;S<`uf5Mt g.߼͆˛ s VKF7ԴtC Hk΄T*4i`V?*kh/]y6 c+qK @F5 /.nhzwwN V30đe$_L w納a|[dm+ڸPwG 8z81NT{VNsx'eL9oN1p<̭<~ Y޷sZpJ:dеWVlGIk:ksKmHD_Yjxz])rtCG{ *۞X ;٦bu"sCB m@!hɥF{}Ϋ>ɥ)JAUH(ɐ|vYF{UE!n*֛-p v#/gh&bp|Pd*xGg}ȹ/s6ŦDkC#+ }RW )qQ:)lE>qBOp3ezg$#ukW{_UK *D+Z7$V }E!q~jCx錛| dGAz+zgN/gaڎ'1;D!-d0A r\s&'J/ rnx`3T͌r3cp_kd(b7$L4,خ;V `x?+n3o6}SL0 m4{S@>?gɜ0A`_Zt(d5:m{G4|KUڻ1C]{vf;3孝|c^ZB-؜̦o& lG&%QK ;Zz/ց#5y#ཤ#&rt:/.{Ťy0FS@rf;$^"7 uȡK(;qo̸DΡQlz<դ`]ÄlRّ*KkgtC',T]w('' Z{& {TJO oXHT<:09!Wfv(WФ %  ˊULQ06~[N-Oq>"F8s ps:62dFg,66aF|%|d ]tp^Йllqɨg BZU#t+5KN-(dpQ] FR7lz%nސ8r?-XMQT?(d!uQPY3=(,{E~ZF䂢 y#_)f|dM8Y܄3DB_阑1!&< qyum$D}wR (fizv .apYG!uKan$PbN qΘ+LSIΈimoTȄsp>2^Li^‡ft,N߲11{9XFqv[C%!i1,8h 7u*ˬ @(|9Ήp2l'V U1mνC!=ab+!~9 qsÿڿZ/]?f Vfv-˴xZقklW .W5IkF,qweLJ$!A0z [|˷ck§tWpB?0 xWZ>F x6 D2 ,_!GUaʉjC :t{̝KG|CDFAi2EBxGΌ9D;1|s)4"1TxxH9_X4ɲ_cGFR ߅ H$Bqݹ<mV03=cMXTȼgcVAaW)OLoГv=$ӌ-rV L VTZANqE/9ӗqiN_wׇ2tIH@ %9y5Λu[%AGR.f0G`8!|28\DU".2: eqqFK|o-2xu`DII\416<4-D@>ЈWsm"'sNWgNcZ׷s8 }Ƈd^SCtLllqMe]#+%V^u7>mdwow(:~8K!byt0Z)wZq,mAeMçmeeaJ'}ѮL`yE"x }iW}"NC=c\ 肹uTI#fh6,?,h904!?4AvΡnµOF&F `Q9b߅s/O䵦@_~ vͶ[y6h_цo9wh1*Cޞ}&6'.'cꦺ}+n2,fN6ovA-[F0%]?=cYD䌼.OX/89!6%qyI.c+{to:nfa'D D@&eξ- p!tTyKsgn?|t\^P6":|[HF9i0Ne25 L_Fg{-;MyN\\LIpwZPd|m9m 14OnmS#J` }nV5tqh^}+5Qk!Tt$5&U.m4B>E`΅5Lν[x+<w45]^3)ԑX?W+V70>D ηLbBeϤss5ڽDfI=H]kM;eݡqڟ0vp#?c{O-<5P6g24M87ZT,7SZV;[۱v:@O@4SKzzk.x_Z o/ Wf[jX-iBmsG [!QmƁL|j fؔ5u<&ʾix\|"|MxѩD#n2pgD(#XtĉlΡk<sUCKq΂Ȩ\C4˦̜ZV2e y $xܺX[aeXF9YbJ@Oڙ)} |9Ё9qοOfչΡu:W@k7ÿ+ y/rcN3Z'0l|yO$r%|YO9*mv/ygpMtd,tp7ɠ/*Y#}{$jy,&au(8Do,U7mKo,{d{Z {㰔Ķ#!<)rC׷l\utDn\oI]ܼTx$ּFAצ+Vn0]jɖ`U>u\ҞٴqPc'{;sirf|19^ $I߸>_Vhy^(D>UGN̹ɩ}44H@Ӳ.yHW[1V][ t~]kx+ #!zePgΡm!N/ 炘f|pX9~>I'0%MbHO%4cOlbJUO L(?[41p9RFƔhC3[lx}\~_khCL +dϷ`eMB=28|+A=7>0/,:v + #馱]Y$ȣZv{1vEY'FzT63P="=_]-y5-/?B/ =- "DF!LW}6 ~J$IyGdr]'W-zvtGw[pL1vqzpT SSW켣s띞s  ސSa?O,=s1-M9g,k(X N y# tb)Y׎[eū{]c= t+S/dĻBQ@1h0T>ssY^6n^E(?6nr(0v[rJ:xB$[p\2x:Weo4Xp1T0[]ڱyQ%_v" ?[~ZGsX >툧cd(S7R2! a5 ~boMGB?0ۦ2oO*ؼf&oiXs8@u|0`2:لsI7Y1df #L  4)#4- ̉+?+1g2_Οд>gx+8P"s<9sl+o,y֎ᕻ| cT;Qz*"SaI)j"俪ߔI89sss 9/o1 _`s#?s$ R{:گr}'rNۛ#"CH_9?sM*dW~L,?qne%ʛCqNVȊd練GQstҲf qBf1]ԧ) $E?A&{e]SZVJ|Y`*os2v/8/-/+w7q^K{?-ߛstoǑ{NMK ƨ#Ϋ>re*۲ٓ_V%^cJv9YszZ֫qE ws M(^Uv1ss~sWMڑ9 |##I=M%I{aw: 0ŋ$m:'.5^x;6%ߚsFNP=C,jW\ 0IBo9%,>'bA/G9E8 Lf#xC`)?;#뻣x3x { ;Csڅ87ݜp(dϩoǜ|;{8 eo@!?^̇6C|(Tp+wr͢.ޱ$$7j<(}ع%B>\vlxs+RnN>}?T^捻dzP_]vo֮&<~~!rʹ7dG`YW%H<!Qfh3=X߮.vmѶkfg;U${dĆeG?2fu|jtN? h,Q!蟋ץUȫ1CbΡu%V!pt=WLm?5s*GtM+({mR'9BfSͨs]Ă8G\b=QgDž9/8I}Ǚo{d9z!qf.rِ;SkcFEn4AV>E@ >CYtl~OqsFWj9Cz/m^2},o^S.&B>7n3s+TLH}&rݷ_Dw@'c]w_;'~ˍtZhd#n8Yp7?siȈLb6\"ը& jjsM8gЏ9'lO[%48̹mS3ߓm*+mm;>L))3 y朚~ sssqVz9l<YwgpZHd!?<*uzȎƒ q G?o'VU\tps9x٨2ur. &j`GWO! $'/D|hv"cw.cdzbYO0ͭ~2,>~ aF=}0!Cs̙C3T$ڛj#'3Oj+/?xd(6=+3I.TkjC4W{G=yvn}}33jīR/ȴ)$72\KwWGѼ~+?\S L%9'Bu_FL>%'$)3;XL/8jJ8zq͜+*cbBԧp;S#ٯlK1F#{ͣ>͋`t~"MP7_^5);^u# Uhuyꖟ4D˦$vo7E 4wW5$I*95U yʡʊ+hGv-.U_D΁GfF>ܲVqɷrNe7}.yLW$[S? T~>׿pSo#eҀ4}Ο^ #7K@-ݎihʯ`}[+'.BVy`RXs';>.#6]]~v2Ɠ)2].A*M0JTFC½Q1o:;ʹT{x9fxR2CG[BcyLO&吻hyY28jU*n6 !@&j2䠺6@2/8 AOK8V>E"rpUJ%)vJbFm*??7=!tbV6s~Tsi޺OZD8ϛyR][%˙#8'Mx0EbХ gdtg ;pbe9捜E!}C9:6$g{sNIs>3 e4Pu1#m};! LŎ4PaM!?'Ѽe=Hs\[ENH҂'22όio2`g]HIx$Rwv18o ft|[?|<h ΧRpW7:eğvJl 9W0GG#ΛW1gqoƠ1ۄ 2j. 8 odo2`)|5ƌ}uM6.ܠȹ9Jf|y2vFv'`Cȧy6>{R_lLqչ01C=Y5Υc{g5lK.4'vz 2f}a&ySp#…}p I+T2pJNkX>園HyB;%59ўMg9Wr`\BДSۧIQB< $QUC9+9b9Ox7Vlrg6VGlK8PuY)C9-Pv΁)A-a5a[1an.D;|l3#UKCV$8I)|Lr3:[~*Gx̥^-g!9W =-GVwȐ]h92 L:) Ms*tDTD ɝӕqp$o82pN%%_B4GJ|̼\ HȠ@SΥM) rNf^Gb5s!Vp=1IW]S+C|A\ҠпjZU LJw'Q?ft^V"aI2l3sV\7Dx[49bkl"!Z{D;䍇lM; G1Je݇T)ΖbJ. /N1_\4m@ؿOiQX4<yul5_oVCOGWY>u|$ԩ3W1zDJ5Ϡ'O>@vz3ک!SNOu^>R|q73\}ehSN<ߢD뛚SMr)`j&N#UUt1J|ӭZW/mN$Ln-M)=.:|CӪ۝( <YkoX|ױ~np}xY-Z-O'K;ǧ& q揭-M'QW <91?~|I,6S45r>zd\{dH]|?694Lz=.OC[ {d* +Qa-'Xz#2>9U޷bQcϓ>'6ÈB{Kƞ[N 6%AVhg9'@lg';k:~e=~1+3'![ F!sݵgMsܒq8HCI & "HV7gz6G?K/UxDWit< WO߰ù ?ئ?iōʗ!~!u;&Y5|y!4^UF!`s| ѵ lV5,&wrAP KHIcpRäL ,R}`t>|`1Y@X|2ۥ6 OI)i\ ] %c(s?K<9k~EWQwMw%|YS۳._R<P3խͅ-my-3z4;˵nye _wR`sD]8NM<^J77,"|{oߨRu_ sk-Uϋv?q]0V +_ۻ& ߟɜsfR( MADvb v) (]QVX@"Ip/Ew4NnoZIpwke9{T΃rJ5>SUҗ0VW/L!ry)zfzƇޒbGGŬN)}xkLUw.H;ɾ+oC5Y9ad܋.Ca ^TG_dBD@Q!"UxS=[ޡ$&/縣weηڗJ=ғ:g1Dugʫ+xQ66SI> 9'TnoCCx)i udb\!x|r.6զ[8]0̜Tk`ݜ1q}Kոw R㞌9Mv$]Q5%6RH3W}RH|ÔNAsL䔅LW?rd w Liݑeƃr=sɄaۂsnu/GTRˆ;o8Z~ukjls6@aj'嚾59Acc^yXV>[޵fUZ#䜹xk=#7 fYZVS,0n|!{_۴{eUsT`sdLg2ܯޮkOb 0f^y]/rt|&oyssK[-5?jOm<|*?K*"V=?1Mn0]˺ 5Jy'ܗs^ϕWu 2IQ^U=…h+ ||~s&,J>Zq'ZZjs˻.碘nʰT^٨ &n?rIgEBz w6l[oUJwhJx<\Vπ9O2Ut:̷O0R/s./v^Kr-"טy`yοy,||Xf}vF)s϶twuz.)-˨u[(jT9g>(u>Ӵ҅?`ݑQC+QJAP1xTV UtGD#z2 !걒I~T/lrR_d_2ytED_ȫXǫp%F49Xٝ(s]:S9?w8}!֜3ӅIg:trOogzG)6:e\,]ER҅Qd:F+&uqlj˹({T1)܁zͿ˯%znTD({sΏ`9$GUi uG>[L}gi@C7➒ swc.]ժ**õ=]e( eC'{Z)5?MىůNJيњC\!uDž\o,ݽYHvV:7LKy8Wq!葾(iL}Pv%_n^p[ZZ[[˒FgC y u%OmSNMz{)su>?6;}6ovV;,9Syثɽ}?Qd޽OU gm`L}A)x›1>9%:>aǚmT?L#7˝_,·+WYe=YbMd[ExˆWp<|㻢<-V-=4K~+Z4GZb.dʨ*0TH.bCǕ$Nrp9[O^tʺO_pEC 2LqVg;7SZP8I~v|;&Re\~[{y>X~MCm 9o|(oͼƜ^(O\S^5Vu2QdS/~x=9Y׶tt3gJ-6C>uৡJ5|LR+i*S*ٿR1fFEuZ%Wg&RIm0,/d; uLRr'`k?j<&{H3\|TKCO:¿RhfT,7U*?| 91UyT>;{YdY |BI%O1IGw-|_ZK jR?Eʜ }3 q #[+Jդ+W"_Xcb m8GsBW~ WEsε6v[r~_ MʋfW(t JqwM0c=8xн ÆKm=-F=poT׭l.H1Wej󿵹=[x1i V?}q5bC?gi9mؚ6=ܾ0s& Evt9nQFc_zsk}\扖gmf+X]?*oWE2up|^_ky|s~{MZ۶h<OW=sIB_\bٔ(G0t (?ǔ3E6C.$ĺ3瘉L ( a /\,M_bKyeA! -T 1q2Ůe?SUODBhYo5C߿}MkJ*0]ϵڐ[F5mJG԰d~YX4]jl4%m IDX6O lF3LFB@AU$LUBhB%/8 ' >N2]ʶeu%./e/<*R+k*Ln\'h+-: UqmKCW{g{m:l}9xmXvi*sV6>Y"H洞Mqx]yVAu9os: 59oF_ ( dl<^Qv 44xᕗ*OqܬCe]te{*QƃZku[їs햎ÛcelQә}WJzZZ .LɅH*ޠ'RSmKHKK?G=Cb=/v&Q|J:/ś "[SI4WvIab_X26B3| W 0?m߱{{Uٷa0Ȃqzu{sɖ@tr [q(Nd|1i4-܃8tN9t,S̑ )W+s O؄Gflk|b+sg4^jk7SQ2POV3py6y|\W R]Ckǧfhpx3ztJqzwyA" r=BzB}uA´=`(w+뻨 ; [xe(;u_#s E?5DIyeo'scG|²Z~D_y w]0BUs?ڿqT 7|V;H;Ni3W! U$w"ᅏͫ2^x.,i'LU'waʜ)HZa\ 8)/> stream xY[oܸ~G C6@m E0Vƒ&N{nMd)E~\i_ϴ^^]68篮w8VwT3wNI}`B3{m}I\m iCWUN{۴A>ea휗Ǽ~ q\s CU䂞L7NՎPU } v=(t@n)ʃgI"uc6Ê9C`9<= +;;Z?zBnc=.-yWChBHk|KRam>6@eE*_̱hj& |O,<7Eh%"ǰ~Q&@_gv/{Q=! |G3i(PMEz1q^QeX2Xo4)Rfm.Dvњ?BI``۶])?kCDʹD|±{:3@Fp@TGEۉc p܆mW~b]Syra+yJh89/]˛/Paw8>@Q1fElo.cO&=1躴 .980HAx5SK"(TȾ3 хy9fXj>Yu8LKV |A1w QiYې/"#b&Wz~T:>G񭗧ܮט8jcI`p\ w\+?lQeQ␊TIz@N&>K5R.Hy3#1? #'#VgRbZ?cD|$=ce}& &ljg˒(&WbJPXx_wO瘝,!cD%C /5,/RIՎ ˍ #M4W#t){(vӲD&o۾#w҅.UZ|fCQ6LN̄jM|sNCC+I21 Є::9VO;(A:9Ԏaf)z2cpzb{VVIOFNTui Ӑ,-ԃ"8:$F<])xJq͌Ĭ\.d0M"(G `4vxXdž z\y.ʒGȪc/  bvx >TQ#բ.MmoSGLxj 53o H Mj~L'q JD'N@;Bt&C̍L`EǛUaUoA(4 X%Ö1;w %fɤ}DDSTY`PXYYPpԄYT_1-CYdio*m>>C_z]c^^ JjS}YkoYʿSR }޼:I@E<%?]:O endstream endobj 55 0 obj << /Length 2519 /Filter /FlateDecode >> stream xioF>@4g.n׋BIcTI*;s77값_WoBRJdQWVi**FWJ{05 P=[w Fŷ"v˶AoT<&dH4(Uڨ(hd$; 2V=)uhHdZPaGu,3jҽFvչ:JUm&FUYUTϼyw*["+g<H6CRuj\b=_THSщD$&z0]vT;Qoˆ'"`DY,2@ o>`eEwwApxŁjZ|,y7o&)$xӫ.ht" 6Raxq]s,JDeS}$R{0`ywͮηyy5ȗLE[7?5 }qGTѽO;L V-zރ_3¦;j^Q3F]xr-jI+D7S6ޠ Z#6[7![^Cf:#tf\r6D%y?H 7a&!Y jq#Ȟ zC#`2de$^Xa!?Oyx|CpF ˴ &b \ծB0=96gvUwi{6 mjלo7AʱIBZnŜ(q9q؅O .KivǓH+R !iuCgwyE1?1C~_^fAhf+/r4yJ(k"e|..)O#u.|hAo2EilѺCL^kiۜW ^A R9Z V􋴒Rv\;.jw" 9j̋Wu{A 9_v)eIh4BPBCV/2jhi1G|j' XO;/Ovtm)A9`}>t| e2Ĥ€@: "uLxf@h#&jߖ }qc X&cϻav+[I@s잴T –Ov㜌#opyJ@=Զ*)#fe@dc Y (Q78ςse&O³ =,hi .uO3 ۻ>)yQ7.GûSsffO .ϵ6AO􎫦ɷ]?:8=*G;$7]Aun@0 YIh<\ɀӦŸFGu2Bi;?BpFxY!=vxt|hvXj$k\}!N; Z6y%~a+~\Rʓ#,=lf،D&GIl(pu|$uݒZ#s*N=YsM3뼍Ap]Vڇ^Nؼ'Os1y-;l|N:uڢJ}|ldW)`r/JQӗ2@f9 fr=lL7,NX T,7xFIGjŒܠXɸ>2A((>4?J` (L}P44o˙7B_~j+JHcrRj{`ZP%> stream xڵYYoH~# Xm6of1 d&`v" jID(Ra1u5EL`]]j]xK-Viz"IT󕏔s3Kߋbms/vAjlٝ]K/q0Y g̚)Y+6uY{!<;xًgN."vUEΝD"ߟ.y*?Mr`%0.{,C+7k2܅$}"! \%BҠJо +,nrqY)q6M?ن\&dZ ~HH=u.c l(SR"Qs8PT[ Sm I^+MvsCBէYSP Ю@pwbe zs:tG'GtNa `0:]7]=(we/':nuߊ(W1ͱ}cP|s( $"P}uEx]k~?@j9uhgأߙI5ˀq]KЬc- &Yſc 8 S]cɑu2O/XC5UkY?eK}gjf.2J9;hCd(18c>m"1eaFDt#s @=7*+BJxh~h֩PtuV"֧m8V1sS'Ju%n>"7dBmˮyٗO GU+q0PS/v|pP٨䥁$*jN\ИW4]nAT -6 f,! 0d2ccP˒wuәF"gu#)9VY[ywEY`̉MXVKvK(b g뤌 I3=0x^@plrW<HDn sAX;cGv,[,"D`s%_=q@?\?o\} jhHq:%aq$lx/|@Ã"LwT11~l.I>RH=>0!])v)FDAGŸDi!y|5olL EelζYҶ9򁚆$)|bCѐ:ζ"?MC=%XcOCД`ԓgĻqrCk̠ qKU٭JK1KJnoCL= _ ӡ(ȱSb}T=N(㠖_|>&Т:v~LGas邕)8&4Pfs]@oIML].ɶ+/<|ykMjVls:4{\=@wz\ɫ}SM8ؕF,+J5!QJ;s> stream xڕɎ>_0 L,] D9E l8nZ\Z,rƓۊK#+: ޾y~ w*)qN4$٥aG9~~ "]b /nEՖx4G%"S^Gwj`(b܌偫g_5Ro57>t{8j7ԅ[ k,/OS"?(*a]籨P8 VxDg?}FXj^_8x /Z=~\38 Q,&L $"Ji? pi?S0C  DdOx AO⏕7T_nM?no28c9T48Y^@-f6:b@Ufؠ`yD+IO8-cz>Js?ֶ/t'8BlyK&8hAN?,:>Z^ vcϻc1>-ؚ 8;Ӊ^yWjTH;b};zۆD8UvO}հuK$RiY,6$ψw@J]vHD{>pR(xK6teI| ǟVA'~H(7kt4Юcr Uof߃Wwցހ&~GkInS?"N .4PyBJ,ȟF݁)!)=:OsStݏw/~_6w4=jy3Xջw TOz3$[t;P(X1rQ.ii^J9Wv䰰m^n9)0y͙5_9 udK(.xĘ)wj9uuMP$$ Z^ӚB }riD' D0ԙwÆ'8R>/䖃b+Vz r_`\" 'c[26BFJDP?CueE^qH `)LxCڣ0^ N7'r O ע*Z3s}PD+ZCPRqTßUQ>F_ h:TC`0=r_3tH iX%]_KǜϨ{4˱ |Uv`Gp!}`TIuBfOu!|2L^ \x:HAf"dN$6=yr+[ACdڶRuD;ehZ"(q޹a{й=ȘyilTQ_1%XyϛeUT R`):)<]G"h|[C9:8V@/!7kV 52ӢjNʸaehN,V-mRoUu28 TL}O.bKkumʺs6= "C8 m^S뷸t|æ@WSo_#BAOJTVK16d%v3h55`j>bJq|k1^r-ʷ` 퇷R) 37XN-}qCWz=Z@9\v;bҽ"ADT1G@G-fr{5aL'/ˇL\y2ȫNbV5B$(ùv a?XyMʆ v߾Rf-؟8 ><7F/)l ,&.Q5'+/4S-M}5>lCAͻE;v-8H9cLÅs%"zwe`Ap.d-)QcurJцϥ:z%Ӌ .Bfit,:Xdpkl,Ԏc襡PTCjxrISo.fl4IJhڒ=DVH ~+)/  OW'3_]#M$J{_qbNV  h>hpNK +g+WtdAŀQ$Wt\{)vԨo5{Ƞvn#e9m )d Ńh27ՙ2ߐX37!7NK1qȑq||ffs/sWwCuM(<}SQq0hQB}q@,2`j3jpA4ibou"m 砱_XCti#ݛogj+@쯹;lԊ38)N4*\ wq*DsH 7{me/xMQh*sKE+0lN؞揁|^}q\<:#VK)W:@ٱ-ĕI 3 9=vİ|Q)hGNca͵c4i~r?8޾ - I l<_o<^6j`}4b(!D-c' N!vH~\nB 1 F[0tJyE<׭\B׶s$s_ρRYTZ@@M} |P;WsM/۴ʏt?0+_K4 !{1EkWd.fm5 (ڈKѸoS92R ,f!vESwC!i'p5܈Ĝ<ŹR| hD vIk\ϕDp~|C endstream endobj 66 0 obj << /Length 982 /Filter /FlateDecode >> stream xun6Б"._K:zk^I[=b8_@Ùk&"_vfR,Ut*kkJisabm4r=~[(8䒍[?ѻ \>Y2q?U.BN b>JOl0&AO4\lAV͡2b`!x{6TRnDEB}Xp%̕Jj66ԼuVh^^B,`_}?tO4Zvk߽FpQ).mI2Z$ծ$ih.&5 Q%O":Ή}yab\z~/[ﲺ[ݺRBFFuK-qRp\1vʴD^*VzL`ڊ"胛hg,g^[bA%IJ!F9Z 6[Ҙ4HΝbTMr*L b03BO'VO;(a+ Y*æ֯+cW '.bDfbf4`?Iu {pD- v6|"[q1ARZmpnyBzm,H<үí *`&T2U'qːtEW!y-Ӽ5tYZkglO eCW\1L4g֦i2U5\_[(Y]FuÝކu]5ගvtθ||1< n҂JaJDLV\Io8=ɣʂrQ51o[XIa^N9mU >U d|̥ajD9-נ|m{ tkNayt/o /^e`\qjFArLTPH endstream endobj 86 0 obj << /Length1 1558 /Length2 9315 /Length3 0 /Length 10358 /Filter /FlateDecode >> stream xڍP\- AC6Xpw Np $apwNpw N xpwUT9{uWCG*a 5B!pVN6!<' -n ێN ك`H@G1 N>!N~!D(L m6(=:6?+g# a1d" ApJ b ;99سAab,'0A-Tm@ƆNxkӡ 5;@G5?8@LA0My%-'YO pq߉?@1 1AUY%63` 1M4>;M n P?vW@nf#4,1؀ p{Ia 㾻uVodn] sKy4k3| `[[Nǣ8>* vkjh+j0?NII3?xw5c_up+1>FYF]_"`kBL(]_prz#O濳_:X[gۀ]b磂A.w_cnv!'?ȜEw 9qc\C 3>3  [V7]VH:<;Lb} 5:X$;SeeԩZ5e /VJ-sJɇ_.Ȩ'"dKR:C^wjaWuLrzP_!I0ULP{oE^ov>c&o5 $A%ZQ =Ʃ{g;PG#@8p4J|یd\79<)M,* "C"[]ODXɛyP+nv?q n" -SA}3ϖ֬1چRi-J O<%4C-ˀzl:ILN@z9ܓĖ!-'aڶ/[kMՇ(c\ktsuqB(q+^@KO@'9sc?t\Zԩ]4[^r'x w D^& 4=1+O%#8S  ASjs3a4}4C2G;ӭ,8I/ԳM#!3KTWúunTK9|cpʿ2l@*QG9!AtH;_ٺgL*0|~\u/9"$2%+qg~ݷJys?/43gu)*r>;| &”Ob( JX'7JB]KA4b{9 Uh >ARoV[I1MC1"2bBUi}1c'+A^qz`3zNYq5Л>_KWg(j1\#Zd?R wc0\RՕ;ޥ72M~fqޯv&1:>whöB>C+f#!FO_.}UI(;4+[XyK,U`s>4͜=u qI nI]ΡBG22P,T9F e3ֳZ\;&~lBʦ(o'O&߿[ϕs!Tc&9cB0XBgP)~:ǛO`k):ר1FݳdKť5{F ! Ms6ViMՠQ#S%9jf_eԑ ,H/Q}!no~O~-bB+0<{̏-Ћ6 sV|7"Xzzң4_qb'Ȑ?D@|9Iw pŵ5g%W:*Ȉ -/Dw1idƉ(U__Oa _w.̬eN[E@>ɏDR}+KQCx ,qpx ?LPȿjh!9o[KҭGk?aGoak'x[w JcqH8' l;Q^aJJ/ )!y[EO<5u8!J*61TL)] 9Yt΁ MorCb;Z-8bI3sys٢K?t?sL7hẛK)녠a?fO(G[}绤F%ATQ ۽yл)=-WJ!yҙbK[R٤dZ4؂:?fR9{|)93]jLH&c PzSh*"mMa^V7]v6=)n?9̧13(>qC :\X4r}<^.aud(߆l~-]= X)*G7ԧ`>{3ouݶEIҬ(}:Y t/] eֶN)\BNlGg9f9zyڔaQD/+"7+ xC6/]ghXXi:#߂бSۄ(z5-X]z{Rk/;i"ܢ3%'UR"):`ؘV(XUάnBHmGYF+p VvxHg>U6sr:z :ib2+-Fy%6\ lGB0¿ytۨx9sSnkd׆ݽȻm&Zʰy˺w6[/+9BAb`41 .uٰ/Dwnm|{K:ЄDxϓ:*S-™~;j5i2.QÒ7H n{'pC f^0>lY^ϥ"kH3JL{ }Dvl̉y/ᛚXNb^.I>g3+#8;/>^!Y6Z3 OG HӃҿ)W#C':%߸q~YN j*A lv~Ȁ&m~+n=ǎ$3ZqVv'=0$׶Wp}q } #=_z-]Xrte!ͨ}{)>9O{tViÕ"̶}b"ςwgtXvi*JKˋ Po5z$ܾ!DRj99ru:+sָ1s& 3ZLI'4B)0ՇY.BPAu{R9 mwONz9o"-TVLūh,n4PY•LHw'di5̬ %hAHn@|t!ttJ$nBG}Gk9?Z*.Rpϱdp`C&lϟʬ;|T~ӹ"t! qQ_k0bCK<= xofm1c]q8t̸i/cfdA_^8x)t5> oawW3OL.̱*=/o&)`b6I{s w/y =vkјy?H֌F:w@ ׁRxeM*}tŭ+m7V<]IRܻ[sXtjZtb]mı#uD, 5`τ8uQ{zXvːk$⏸M%rڲ{c:ʬs n?r2GDVdOqf*n8q yc?l4r.?oɏKmK>{] yMnfxVT'`!Eѵ\i0b9",:L$Ѝ6)A䇰JDf#kQC{Y!-_+yÊxvԱ]{TUqCf$9M l\1IӪu`y eIOީ 4m+l>#lġv72gv&u ˼Zxr.ڢQ|bqy-ѭN~HxcG~#΃yo8+E:Y+w$)x;Ģz@d%w U~F^o;QBI3+S*y::oaSLY=:ϱw@k…9~= Xeo$"&7ьҫT/2R;V$L,$P{㒖w=Ж=FF9Qٴ U*BcG_TC7o^86_[$v#;cL1gǭwϞ|UYgkr&7_J -DJ/"Ic։fʜ1HJ48m)+Tt y)ȧd,si0@Ctpdx5_[%ZZ7mߒQNgI% b4uuZ}j< e4cp|@,MZL<*p\o{]xov0d[@v)No! սZ;`;MN}pML[0)o1vH_GZ Y=L} YJqO>ഢ+@#YFoe{ Svш/p6Sn *uKSﭾ%3$[.H_ ~mԪ,Ӳcg30:D[fx IiLie%= @m€0N,a/R|f5GY=55KLƧ!tlwù!^eܕ4^y6ԐyN[Ǡߘ0f ^Dce a4E#r| JUWۢ}?)R7ٙ{`Xd7k7 R-J4TrV<@FJD+CG;5qL2>%_J,7IG.QT喁 :5ʾ&F47ё+wmcoԝnpl%VEzRNqy~;\#/=0#06=3yx-A ̛PJ-|j4 C!.Q+˩K@`ˊY1 ;C-z9 NǵBv_M*G,"mɤ o9DRB.Ql3KӃчQ<#i4Kr w5HxL~%fD{4Sw)8Q,O YYQaAmV硦-_?U:fe V_GӾun g d:gdhY-3A6~oc-N%<3wʽ "7V:u]LD68xxNNa5q:AbpNk;Pj +M~ i4*kg y1Y+y'Nvodx?ضGZohS!z582>d%:H1jw~}Q~[_yx Ї^IEdҜ0Y o0ҘG}bb׌qyowX\I>od܏:=/;O +Q"iRAMk`ܕR8 K@[~jm u?Sr~jݘQ P<%ICl!5G`$^)ѻ)ԙS#$8eDgLCaw9y64#{|N(' ~֯Em;;)C\->cZ1ʛK.Z:&5 ?t*ПT?Pzl{VdOШҥ@tE8G"(f軗!,:IYMR6Ptw<Ŝ4`}JaCim]:g.|NVyNVB½Zvxgr{'n2`eyxCɃӆ *Acڵ#6|忐X Q[ ?vTŚZ v:TZ UO0N cWʣOF3Uj{h?Yf6.QFJܮ'~H:Tܾ \DTO 瓯qvvrj}U0%hW64~5cZl&ţo 9tj/g$L}XX QPQOqA78%Ԟ|>7]xWEKJ2(w$Y/-i L#CSO[.u-\k nc.-HBB.Oc2X7avqoo E(Z3 ʶ%+ߞAf`MԿ8,V\|ȥ|S뾍uD7f&G t+m{+*lm-ଗ{PS/ָ1:OKOZ4{jI#x'ZɈWTlx̙^+^5+, P򑁥K~zflp9"؃Kwt» KD HV9(k6qݜ}Hq|{ML.8aNvsOT?lq },A\4 P]VpӏMOVԉG*GꆼV@eTvpI"UWΡ+{v_'T|!eg4IJRқ^4hgpR3<԰c/l<;4Ɇ)Ϛ,kϏXňTPJbI#ʷ+Ui\SgoXԃC1q/ 54R܍js_m*G"!k;?YoȣdznuhvŒNCg3dȰ"̾N>ވA*]Yԓ:.3Fw\<^Fr8?yfZ 9Twz! !'}i7q>3f#2ϵZryu=ٗU|S0ÇR铧s*_ӴfTl袉:ñhRoqglb*%}F01S|CBQy\15a`l;&~""w(+6qGI/=m0g`NGf_B/rXHӬ4] 4>7}=o? @3]&k MdQ?Tתd&m<̵Eq=;7Kږ_H"GpAf7?(bnv 8 ~rTl-r\\v +]DW +.a{\#cq BBi+Ws{ZDf|BÁ[&E^E$imԟZ< IƱTy7ON=]aH-bvhl^]5GC l`ju%T]Fʿ{@fܹ8l5 ERu DIҘk|c-f;CU7ӄCӵ~Kxhśt-R5.ʘm ėʶnO[[i`_hEIsQ^ʙ9@ I a3UW{0:$:~7׌?9y endstream endobj 88 0 obj << /Length1 1535 /Length2 7923 /Length3 0 /Length 8941 /Filter /FlateDecode >> stream xڍtT. %C %53tHttKwK?}uΚy;VbCe0$$RQQ@\ '&-Nꂰa  e佝 Ptu`^A0 ewHCl-*E8 ;yZ h qH8B]l- 0 iuЂ[BQtpwwg8".֢Lw[ @A- BUƎIжE%ׂ[!!.P C{,.Z 5'(/c XfoqXX 0O[5 PUfGz YoC~q:@ dJh ] `G:.w.,P;?i[}=9= X,~aġuv*Hmr/Pg†wxmO'%o'q.P*aK[ $jm w{1/|?|[K=e 9x|9 TeXup7'7 ?èClNo_po'>+ev03*PYnY\(/wBԌh=k]AZkMY߳ KnZ"-l_r߻` {/t fa 61}.ţ \%b`,z?[]l%[D>Ӓ|Ej? ]z#{NE`{^?†N G1УS_G|URlI 7Xׁv+tHN!Tk/m/P$7k5;My/]ZjCK2Ӝ͋*3i額ٔxGtf`Y)My\sV4 rN!v(1wS 4 n LTK&nJG*cܞ<6}#"4 gV=QPɰ3vӅuuk>|M^*}Lna/`MJ]0\<pNz4g5TvCthKL(N@qYxUqȗtwXj84u?޾30iX&޵: zd ثtnqZd Lp4Yl|x>fS):q}{^ϋ,z.ZiΫ!(#Ӿ.Oٗm3ϻRPB0N׋Pdl2ٍ-D<u( +N\jc$I{YCeXU/󦑂d /]DCPh#&vWc &&7keyT 8)`/ p(0pR 'M8Bj pRxwM_DQrСC}QW#n:$ IeCIhMcV;*ZnU& im]nh={0|dya LNm,21]u,[ңXD K Eɰ6,(N\!P\ѯY+0#}mBCrEݙː"Ռ Z ڴXmN6<vkSY}/Aǵf`w$5"F2k'ceў)]v'<ʹ~KmUN{/I:s>ާR! AqG!z!cJL!V3iPȀ°o=e<^sZa.j#F&:vk ^@ rA#n.5}FC 7yn^5JMQV?9lPXI#܈y.'_w,ݕ܇kmR@q<ի @zrܐV/X_䳣543Nɜۥ n&d ?}ѳ*ݧÛ8?nҁc"\BZ 71F.,6>,tc7:CŅ;'+`gSeį\ _f K>3ՊfDX|$ h-Я{rNXKQ'qv,GQAyjŝd$5X $95GWOgŋ[$Hh~I%fYgϻJ}Kзx̾BmK~)O\Q0?m9!P454^sUwx!Y7Nģq^d"Z\1gvEuݸ(i/&8!2ء1b>FAU^a69+<{0KYWƣ$K77wp蘁n0VF>ec.z\#1sa8~ylIrAQJXK ( *5FӨhy@OVUiy9o+q'*VfyR5c/xW{Kf7g"+6t/}wŹ#hCX -CKa͇BG;7y-Q5w;k'$qoR |'SG~5xO7 k*B&9fʲH*+FM)z7 .if j1ɬmjǹܮVнΧ΀XyF.t]f6.TQ8$xk[AtwOzݑr!/R@ʕWտQcƋjt+.OEdXt[:;f´|qz1E2=H g8^j}. 'dM՗ {⁴:m*Rgɲ%D]'77{?rk*85)}5aXŝQIF4,Uy8@yl#I~@ǥѷ n6M9ZP vۋ[~k+2;hNɤp]ARsF*<;;0! b6Kc@ #f"}@suO-10?@BW[&v?/t"(0NT wJ3Rq2{([Y:GcGL)D[n9ur)ne@>握oܳ疪=\FzشIm3\}:5tq'lƱ`Rs>/2 )A)cpɳ-lDŽ>ūuj6:Nٖ %c!9[^z:ZTh}[\_4 >~?S}Syxᢨ Mo?%//U̯0yԓX3J lvCP3e~>N67Ovl;L*v#l=L8&{- Uבx@?^6!ir06*IF }N'ߺ0Xjta\D\>쟣sϹ%q-_}!U~+Aj!#j%!&ʳXJ@q1wzأl0J{sȻ7*xI&3^?-o>0j&=kN}^}r@E>ΕKir P0YY{\Ys2Zwpn64^qoӜԢg˓܍]Mɽ<ۣI3RM&@ ô*@h{΋vz?/*XmY݇J0K4A i3QEҏ=&y ^,\<:xΘQ'rio$)J0j];&=s[?o b!ޭ8l y }5AnL`w5Fe2Ba ܰ> 3Ty1,hM4}sD5e'ΐMy1"v@SHaE(r*~BHΓ#[YEǎ-O3m5/vm dJmpI$=Q&K}G"]CX'oڻ􍊺'=4y~8kk)Nk\7\H"rc2V2eu%߳J> 6! `}оiϨ_-Ѐ$J$|PQ!!E]X_Ȩf|N;)ƃ lvzse9z,0{'hׯ4tsMa$pԦ64`MK̮V~%yZEt1|v'A[Ջx#XA^'%+DH*tCĝ(x!e %ϢOrN^{~|֐^XZ!K1-}] ?:^=4Yx :tϤٳ/62w*?ɢIY>GɌs#|WY\دݷK-gݢmW#jX&'mC{uq5uGI>JSmJiP՗6묎+(}ee;uLQ8sUajk7ߒʞV(^ ݸDưz`۰+DngL9zIX9JFqg{l blyN'*:xn7c-;;4X!|(UU%;lUʦ.FZ4Ja!JƠAz&Z?~ =1 d액-zk)w2ܣ~cPRuO!V/O24ٚB쨢'(Fg'™V54ɘD]N N9h|GteTiEփ%cs<lծ Ga`M֌\yb\ź |5? Ngՠnq%shH跎 ~iQJ7э\<ЧoKT)-ԵGܕ55R((>0z$I38&5Fkc(mK,$,l~*#&r#aCm&@,.|@P$rZ@ Aa^q^-J5-sHn+PJ#CŠ _ e5:CD𧹶, d(2*CXFT4;`O 67TIL;'40g=Ù➁^Ě8Mn6ړƨϝ]2B,iSۋwWe8<|L5~ڋWҸ; mf=o}UUI)T~Tlb x8w>#gF"͕Igam&SIx~BgJa,_F%& \:,!L0>f6Vl{0OjVH~yb) ! dR(\D{(}W66 mUܞu >@))2w*Zc_P{غQ"5%3)9_fRU<& 7gĘ{&%:1ƟPu+&x_yAK#hc%ԓRY^W%Kg8Z"k#&]w!86 w;3#ռ.}J-}9h0o9Eﵻ+p}N<؂o!0sOrjqA}Sԟ:P،&. rYc%GӼ++DJ*f R_O+o@e-לjI8ML{b52I`H=WBi )ܹ)gG0XWpGMmyk4=kUiq#eG4Yry yEwDR/F}lį'1I雏0VoNӒg3qזtw,w8+'N\n7dwZFٛ84j%ߕ)@ݩMqIpĶ_3<:@EMs n=-tɳC6a@tk{O?LD:eR˟3P0suϸӅI̭.#^ P# )y}f$6b?PxjX#tfTvzL]2ވ IH=/H$ >a˖q{ƽ_g9n|G)҃Ѹ{&<Ȗ5ޑx7Or8澯ɫo f޴Ur$oB7v01בjc) wF/($]]E5UP1; c]}:4p-c8g endstream endobj 90 0 obj << /Length1 1444 /Length2 6575 /Length3 0 /Length 7559 /Filter /FlateDecode >> stream xڍtTj.t *-0t4J0 )! % n AD$$~|uf}/!iUC"0B@i098`8LaEEPFCAkLi#-W8@H $.-$! @hi h  12剆c_7 $%%q;@ A6cu  `ܲ JZP]"D0 ~ 9AW&@0 w 00rኀ@р((7o=g 3 F:@O` Cj0{ An dsM9]<0¸?K˪2 O){;,4F]*(?R3e _WBE@}aboAB}'b!!lv0?ѯa09Z{B,A"WP1ݿxńR 1 Q@?YqD"Rv3?w,h4(_ +M?4 ܿ{!\ksm {g[51=PBn#E 0`b\28 C~*~! lכv~9\^_#!?7LXLBAk! Wav\ ENT\ uv5 QO AoX \)qG:+Q+KJU]B=`)$X&ء:Rٝ84~Yta2OEf2X)y-ª*ۅvS-aXs~sxt#/jzXX7|.}Lq۵8r]%Oܻ=jzJB7*GG= (ȳɚdMgK̓|x*gM+KHIF.wXqF9ohO{.R'چW^:'}KNinFA\gQ NE Y=4%l\&t5b$Rg95^ܴզa5l.0VNuh'}EF)2kU֦6v[F {Z^cn[po doư ĉ;.Pn99G]MT3>S&:=>+m͖wϫ4%%WJe/4ccNpV]߯H,Qh\r<*{{0`ע(+PbgxTDmpw8aBOWu),m'>rNA ûc.yҢClVIzX)+{99V[4N Qn |LN\R+3 u.LlS#E[)4c7@J/G-J b`|AQo=*H; 6Fv|[zZ~ 7>b/ C+Pe[><W=TmWo!D8f<ڄEw-jeG(dK+ 3&"hyMj%w vDJhcq\Lȕ!U$u(FJɃw .!KA ֎/ ڳGu7hB%Gxp o,禘HX~t~u%|X#l:Kf}2&n/o 'V͉gb3f|ުcHy4JD5ݡVLKZkPus|Y&ohߙӄS.j@4EWi$WXEXZ䰿٬PV<݆M}C QOPJZr6f=WĠrc-3 bٔᖖCsRͣЫ%"Ū˗ >ڑD0Բ-Zd aLrD`bAhC؛qu:{j,>ü AQԯ#Al2 Ceq,=w|&;q8VBT>VFD6{d͡D/z.b)J)$h\"/HW\;?)O=`yltep0b.A>({Y'8d]1xx:-hcLm1Y}gCwK&+5htIK2?|dw 'g1ΦQ+(M2J[nɃBRRsl⅔7RR,;)>TiRߋ,X;s"37V-."UP0DovYүR3T ~_' 0*O6||?S^=9ϣ|}{ F\\i6"H9Iț8socc)H bff=9՞;T hy3ZIW"lܺJ]̽%G,';(#CJd0MR0,udTmKSa>f=oUAӶUc_`NWѢ̂6:W_>+FUPk?2PkB3But?3a e0Yon`תk0{iLVnQ58-PꔖTl'k?6QazVc}.BSQ ŦpG3k vO`H^c"w0t 2b}gʏxUKSW o؈AGyuUnVٟ1.ڥZxO'3{Y)c&[~ͤ ^}/qڗ߿fO`ґǩP` :!UO{.!Qo7{Lm3g:Nwvw0Dwʵw0d6&9d^d% )L*RB^wVvf%qa]g)Sұ.0]OR68.= Vk҅Ἐ:/0?lCF5?b"S0n A<`UQz"'Eo'&cx#j.[VeFW'pJ3Fe08)!-4= ag /vM{OGpL[ط~cB&~Ka&#Zo)ry%dS?\J񌗬a$m"0i^(:-j̢Teb@ǼOp4ɩ÷̄jn,&f(p=?94 ̡f٠$l![EK>E4ESGⴱ0'BջGH *S@\D/-'NptoRmhk${]0^sA䍔'~dC)7cÍϑn9|59ca6j. 9-svԌXS >_^p8!ْVKL'jDm;㏉"1.fdC ?$CH;ą}yꘜ>4ͥh@^yY"k#{Z'`R]|wa4ЇfS٣^ӉMݶ{m4Hx[:Ob?%#rtXPَ_ ;UabO'/۲TQKe*ʈY<GV%0B*qݓ钫Ɲ-nI&-;D-L5V Cw W Cx}y}_$z#`@s {= Л7PfACE0 TCp}r tO<W sN!(;_< R7^5 74? ]-Q m4ݴ@s8+}{WJ(b ر2M?bSK_M'/ry8;#8Vs ӟ)wڗ@)-xXDUu xtLyaJ\&(M#zyG.&/i}n۪H1J~`*'dͷ^1jz #OD29>'Y8F@Vމl1w5],x3mpoOON}%U}ndDs*ĆΩ`>(?!dNbFNtFXZvs=:=} 37lRLUER%?1#LU_!xmȹî^b~ȧ{#HOrqfE1njӌЉ{ݓ'lJSjY36OV74o 4S-QdaTM|>x⒒o/_n~WԁC<@NjD>O8cR1yQXK0S:Y8WJ &0R kVm6`t 1>QX)C~%ȲzqkTΖ2b+Ai1#Dbr,8;yW {|lya5N[9!)_SVy3%a> nXjDG yttUYW~bg*a31+g|A|-ŴKY1O~5ؚtD2*9")~o8Ց6Zr!{{9),zCZx&#ؚa/<{6!n GxC >A:,V\Ά2uu;!sGf=2)zYRx^iɌ t~zEN'ݣY6u QVx"eXU%Eޫ<`\O'WIq(Q3.x'e}%jyyr Cozc\}Q eYZ3WzJ ) [75*l䰑նWSfΤU}`"ИՄ'=I:Tþ]df]boWu<}@U8(NEb"_"剁*xMsϐtg{%OZڔ ?Yjj$wXtVSbxq633^DN޺ d\#4U7iuy>\rp#%ʾɍTIE:Ĕj-;{Q]7%Z,5;37y V C֏ze;uJyJrðksh T)oQE}w >?xÇ2ϲ6#q+NohZR)v?7_u[2 PFmٞ>4c!#^VuMp Ӓ|b7}Oi2f bRz0n>A&@樯z~K7?v}_⑓5dҭ5e}YL)Y6k3jhڼhF&:h1 Ic~+Rؿ_‡hc\:9 nBj,,Cs9y?;%AG>i& [v$wUR]~=NaSĊͲ-%<Mj/Wg/qg8v n5-*uA[}sTÐìXH,O\-+ΟV0v1{̕ |rJo`TäӧնL) @%ϲ)&qjqq3c{UOBo+sʤިap% tۺ(q~<¨̟%xz-REؽ1blhA$L !Vn2kKs=)kz‰H =o]V~a3l^xbՅvaϮ*S^mϐpZ؆y endstream endobj 92 0 obj << /Length1 1357 /Length2 5960 /Length3 0 /Length 6891 /Filter /FlateDecode >> stream xڍTT]K$e@ N``[DiiDZQ]5ks~gﳟ1@J0(/({|/((DfFB@lF 8 J7\Dl H& Ps@Q @HPP_0@ l <GNi:`kK(@irBhm ì ?Rp>G"%-0c.^;i!@p7 W-K' `>n PE\6 8u6@_U q j@~+;l C`@,6-!* B9.$ DWwk8G!:uɊPyDO YnSX0w-jcWgC(D  ^WrOgoU3`j n  ?w@ lXP ?{` x@߿Wf(nϿW@[ESGѐw`o>!APPL Z3%*UEҿ vk \Z0cAο n*("h4WY+ G& >&*YߗF(=@6:` _ ((JU֎Go?TZl~KHD` [zFډ(ڀ<~3 !Q!T{[$5ζ${zAUb~"",ӵZDݦK+x&/#dk d6|]|1ز]\Rw*{t LnT߹*62 ,`˱ʜaA1qxLeR!ݍ~Pm$s>5_f HJkLÈyL>0-Zwq"| yep鵮wi6,IK#IبTĜ#yUmCXW;| &<IiAJn$}IL/E{~TuSx -ksOK{~%uLT*+p GN[T=gi?dz6L$j-kmrwrUFݫ,\d$i8V=x?"xT#%W>qߕ|:$pO"ڸ#cU9x(LgI :yйw[`)A<6f1At]ޱ#,默jؤqBƁqQ|f/Zl& ~ Mo6v{PQ2R/֐?'xI5mgY+°3%pAcmwn-^R鋒3 0dqJ/;;FD+ !'Bqi Y}ᴈ],9:!Whf?DyI'vPj1W3<]oNhXQ:N#Llgw]#2*ǃ"O#ȍ[XwO" Зhp0& +:Ic<}o65aˆtGo_9S.ʰs5V)нyS9]'U8~Ҷ>z Tz kQZYFb oONkLqdF %$ҩotNIT]>2❡9tQ[b'< Ѷ:ZcZ;X9ֲJPG7 I&sH 4~U }HSkIY#Bj3xE[{(g(Atd<|VğĝafNyM.50 36p aq$$\i):(NݤfƙNOG.Ԙ.:YE^;C²ÞA6&{vlfπH( `-{t8F&/qUN+/9 liJDw1(&2{CCl[R>=n^"At>j^,{m%*pR|+w$hѤ[ gNYG\ 4pLZ (eFp:55caLo<4[#\5YnSȌF#;CRe7W%$KuP !׷Ʒw j"=D q(E:WP pPr*|iE3ar$4a]PckUW`ιo{{&Dgp ?^jI.azi<8[BrϨ) o;J'O.[J[G;#2Gž$ا銝]dt6"<ޫ%cwfݲ9?Pfʉk`~—({x73]<ᲭXWC;ե[N/1=|>.|JF[wm |W`lGى6Gt~rO!^)B }LФ/- % >M /\Dӎ/DhqU(3^]ĿvPm_aV5sRk9OUteĂ.w ̅lTѮg&Y,;1\֌, z^䳤u9}F>Y >L4:eͺ 092ڋJpmD$ٱ3QL.9Q* pp=M㎉@BjHv1fY:B뽯Y;ro𧄑sbI>B-d^3vm(n@{+n*(5]$|s%r\Q}FHNk/]}Ś~*&7ޅTqDkR 7Pۈf6`:M)^J $ ʰ()b̴,.`0e|g5.OiAx_%+? =u.gn/ hɚ!qy+oqv-jT.=jGY9]{UqCGlBa!C/M{}3(DzONOFnA]nn֕ ,z۠l: W輊0eya=YX3t .Q[Mv%7{DU- رMݜJD*S-hUn]e~E\mn<8AYv9\1By{0oS5ƪbNb1:IQHEvxVΉAd[,I՛$\9qϑjB]]օS>taRUuTVBWON e*I<j~Nz-GRM5"=@`lfꞠgS!9G Av6>9GF-teRK/!ie-BpJcvq#^ʻ_NHfO?Xds *pݣ9Voy^R){v)pznj܇08+|Հu%4$#gGϞX2G̺2dん&N=չ@J{xqR荢Ԕ|j۸2&)G4SaG }a35S^}/o`N 6}7ܘajW3 >XVwyz@p;Ekr峘++EI4b,hCCeg? cCZHtpEIh4}2="S#ր8n_ujeG),L}IT2|D2[/xQ<3 y uN~z 6Q3=rY{BV[ #Sܷp ـ;N4shDp^+'yB49`z Dcl 崼C5;k3:8$jlM{?z5ݸND/j>yzyƢDeV`&5٩c(QR4UHXo>YH5wDll\0M[FN:$7Hꖳ U5F6zsŽc6&,(=ѩ!s1>91f&+?c:'ߔuV: %s]|Uqi|JTfoLWn`}pGM L0w:uDb>gln#[<*| N:iE~f,u:xS9 0gk:m9j ՆwZjVRs}q98 ʊAy L3 h[~ɢub.6q.+06#U\;94k9z\:>IvS h.˱fqagcmԔlI<^3\.].!|QHr[Enm _[Uyt߶Ƀޢpvv^C}D}L0C [KwVeZUf`[\*5ϧ븧*Ͽ$sXR1L˹ { Vr( Y;)\?''aPH}ne6IKT.2hs+ YbzSy-A}%LS 2etL-) /iL hc.rxCxו+|WL;#wĘej?#Uz[ZgxVh Ә2nᠿ%{Oٰz媨[Paוr\+C'ú+m˨49TIa=rT(7M(hg~=#;M"aѐGUgL%FWHsMM Ma?{1K۷SC&S~ڱH"D^Xf1>1J-i{~I9leT/* |]4FX|b`xp|r~Q,4v2FQG-ps(Ӓң64oF h cxҫsL>Vjά%W$„"|a<*vH:uA烝LQX5n5aEA"Õ ˥y۔ pO)c wuOZYA(㫍t #X4pIgŖfɣwP4>=q/3JBϕ/*Fn2|U&kxJU֒ë!{UFr 6`ޜJh%<)RA}hPʀݜévd|2r35r98z+tx!s#NxCSgZNWLKD& K+)sL W B꒗> stream xڍx4\ڶ Z}^g{=1 ft轅QDA J""$$os{[֞u繮=6;#A@I@!vvc( Nn BBp@(yAQh= A0HI! Po Klu4p] uvA} s@b| /(бG@0 KwG eP x@FڻC&@0v"r!P^!p$:4z_`|? ODP`{0aNP-CᎿ0$oc;[* zH(׌ҠYpwQH_)C ` 9\78 wt5  PAms"@ QQ ]0v6g@xc@NA @yyCpQ3Nh35~K ~ _wh9"0TTD$@GՀ;!ާ[?3.\D L!?;R~?~{w(\oZ: 5%]#({hF e"U~G}( k #_Ot_>nM.ZC#MHD`eO>kJBA8g 8!~,H #4& @ F6 ߬@w!?`yt}u٠@ag}'.b(Vű$E7OmSαxRUB=ϧI?,"Ж1>g#yJ,;ns#%%guXқ;%PsȂm5faEDema2utؕ@2)wluUPI@h]4-e&Kx-Uj $ȹRUf +m{4ag];z_z7!Dd Nl mKskfSdw8\/ +f4ԙCTT^XhcmM}!l3}.1/$d)yTQT1dZ:@AN=~O!h9s:lUX1#lXyw~ 7d<*FED-wra\aQ!~gyidSabcEc]]| (9Rb OU֊~"&) |2}|K0qA6)^ǂBx6ZE=\*t95QS){ab kE?LBl)*Z9%~O{\),.w6dkI 9/93w[α)$GX#`5"JTq&]gmx^ZϾ,/oy~^ '\ު &5 Q&*NNd?.pqj%؆i.9d{Otq|>J 9* W/+Do㧇f:8+NJ,_km ߩkBghfi)G9{i:DWf̌y*'Gsg92r`̪?B0 ]@ϘT M.e+\x >N^:GM090Z,:jm7ЊHj$; ߽Ր-jL*oy{(swLm}jl/As*]K[:JS7S۔jbyk6Hc2єQx&ߔ,Q(3N/rxNJ[kf͌mC̥2eZ9y[n7rD'خƥ fuSrEƨzB8{ ShءјFSd.ӄE}WO_eq%6mvb\tr5}$ͧ+!>|lͬ.&:rgMr< b>ċ42+ dI|nͱpf&qhAyLDwd3Q-Plng8 ?R*0)i2ewg% W/ BNJk #[tb;b_0U#Ta!IJ|˂sxqbrio n#58,3y#2O0esӆ;`=)F n#bko 8\QEJ^]nrTkE Hp`|\#<(*|tey|`5nU8gvDž Ro'G"z1NPŰRԟz>8_7IlX <.R/ڽD/ʚdo,1I%Q{H).n]i@ӒdžֵjJrA2_Ƭ 2C=?pv'[+sKJJC~%)8f>a xTkmbgˉӖ)2fm|zٛwg9_q= yWCeE(lu^'7Mqn9 ϫPF$rT\Qnf-/T>5 qZb'b*\Gvb$L|G ^Vݧ5AUY"(=3gއ)EL*-7ϗtPKQXꐼe}>vYD >ZhR^&d6pmYWk9ћQy|Wc 䬾?m(;6@]SrȠ8[LImU%a|gXapYI7E:$xIP$"r/1K)")+ZZ*~3t,SDm7Î&&'CZΫ<  pǓڦq^t'OYS ۥtV>3xexy>4L0HbxeĸUTVS:u5 V{Ƹ"#{@]Kt%( `S{qCJ#塚 OW婩ڸKoq.SJ6$SOh.gWv+OgbM s"j@">U1=V!yAg?\sŴ3`kƷOSw捻2rSV'׉5>|QVXK"kgBfsu盈iӌI~Xlτ<~ =dė < | |}2A5B"dJb_ >gշd/1|Tmd 6:1X|!.tO '4c Ya?mfC+6 >*2=d;P)Nq!xd-{{ޖDDL\oNJ{ 5ݞϧI_og)Mzt ~)߂PVm'^}>v Z'Ҝ],,۟.n:E%ͻՉ pCc^jݺ>'[<88q\ˏmمOeY^gBO¨~Κ*]>F3z[@כ>w{H|3&Jl0&MA:F$ըdE2.<'7y&q$,AyhPiE Qj 0q]htCNCt0  _9u՚?oΑ%xjL?6,[V> ѽ'bL qe]{3:V/|،x=S'JVl. %O:p|F5V67lv#LtcׅUpf} V|UOکm%2~]y3̠INnF%魖' =3ض?zopk%rHIℹlYB^OQ7sҠ*"D_J_' 0_ݍ[Ju'HxTwohC3Еv3|w TGDR*8J'1xKr (wE >Vs v^i. |Bqm`}X+@+}ɫo8SЛAq ]:E0oJ/Hy,L̼]_v|+)׏XJ12ћ )[ s3r++7x_ڤ6uH,L#K{K <ӑJWEXw/1A,0q,0"<(> ?SCܝt'/[p?C0YղM\kp"Q>~ގm);W?uӋ^=5DګŤK,kTn;o # O-Ȧ^l=C-BmkWUJ{,ٰ!ylGCC;(fDZ۲W;C?M6~RPfK+:UX8;oWżXo?Úri,EvE;9f/H +I^1 *:Onvt.Vs4ToyxUǑp|"C] 3q z\-f+MI\n@V<8ãll~!9quBKCA]B+J8>rzZeo b*4K&C/3Yi.Jg%50xwu&>^p۩+8ƂzÉyW| "1uLDHf]dAbK0ݫZ烠-U}qe@.|[=Iw fܿ4a;ܩ"O5Dm5bWtcBy*~[Y仝}d8ν|.+1S{%~AZ*X"S埞#4ƭmqmQ7=x[pAp73UïV0EUnzw/} |`N1V_!2.B癟R.śF9R~KN5c;+'5> 3ky6̿2~ǽ@0/ٴYWSԝ%0tcy TidJeB2,Oou; ݏ ]\SdyN{փrfvζ4%aϫpW[{TN.K- %"w_>uaI ~"8>-iWVAG #ơ/|{pv8ڠ endstream endobj 96 0 obj << /Length1 727 /Length2 18279 /Length3 0 /Length 18872 /Filter /FlateDecode >> stream xlspf6;[ol[gbcb۶m۶m99_[?~Y]ދX="!`b`#'u:[ي:y@  Gww03wPS P64pY۹Z\]]\\]IR@5 )-/ WHmE#k c1 H 0sXg05''u::C 'KXBA@\( +O?hۿ8ϲ6e?QGX p cg_Iۚ86qoBNP!5hZwq:L;k3h_/ Cpp(Z8#?Rۯ)g_MlOw9F5a) Uiv&f5t4aECww-ghfbא0Q""vn,,zVf37Eho4[]3 Ln .ϟ)2P]b]F07?Ľ`ψb[0p4>IRHZfSZwcc,qv뜺H3uVӡE9Ҡy+veVZ5M G[ ,1dү"6i?c"םTp 7C וzhʯq4BE؊ČxR(ME/@OHO\Ս᫣>^^MQڍ 5 %q )K$ nWfCk=4Z?;Hrl":KRoZݬҐnRx+oOG\J܊rgFcױ vύ]tp$éP܃E(P\ /Xy3G_28!"o(rS%s˳`dq ޝW/Tc-Rk7ЪϜe^]y;î*Ӡ~c h]:u$~sk;[6n*-bfAIK`Ba;(pK*c&<N 0:/79uH~9{Y6#c6!{[JqenEGh730#fگnzdfQ:zGO: x.Oabl{|z$eܛcqzUA|w7V+YD:# !8MB(;,2 R>V5U=GHj&>nG1B /JK4qh lYn,+r"#[wZz6eR|{f ဴ́p`ŒVC|*PO@*m=7%HcYUPt=/+:nald|X|y: 0U0RA+4҄m7~V.om=&mF%L2Hu^0N| ^v=JS ~W;&eY!TK-҆׎qP_rMmӠnrQ S2WEuwHZ؏b*!BRc9gϕケo5v2l\0I|=J3 ҙ$oG~i1RK*` %κ)0 = RN0\QX G&Ԕ034umt Wʛ>:{)m?8Me~i$; f~+$ю'^յwgHB|,sJ4%u};蝞6?MT9ioͺF:^7%]<nu[87tbkŋ?i *~6lםk}Vxd^p㢸Rl,)\j9cv9ޜO9xhZE"~4 HX|tD "( W@{zKt?WiSRD &iKʣs4Ci43F{˳ʿ f}LJ1lQ9{sCC+%$2Bzwb_VN{9po$O*F^Ih4VtH1a °=sƀ,%D?LvI .SK)#gc͇fYDYr7@"gk/p9f/K/ ̅l2u`/@SVT4c(hwؤ#$}utt;_ak'҃"1},#1Y5{TT}%2]l& |{ES^D 7xoZb좏 55 =SX8ʯ*QT"l|--QQ ^ˑ Όi@N3z0x.¦ه-d#n2?^E.0lnd2>F)ГpFmTŵꔽtkA  8R; PKMq %;> g%(7 CZ+e{M#I1W)=5 9BL#ēH;T6 )R?"7"sd6.ʬ}XqC{'*Hv[ń)ܱUجR{|Qc)"]ﭪU?\@%nzMv^@@l\OA/ZL:4U~WԌ{ HKi#)HL1Y+pIob߹|H|~u>m+n|䇁&8Ii:t PDic!IR2BHX^CGߚ^#<6 `M:7%H0|U(]2t򷼫qE;n4?ŖjWuB=ٙ5y;5猚^{̼k3xL",dIeȣ0F,GofQljI"/US!t-;~"ga i_#MG]s;j'`(d@2fQW<[em$;LqRhqet jo1W̰Ho9ݝV6HP;jAu֭i[mKCڰ"/ܛcТuh/nZNپX'jz lƆθqKՋmc\wjɃziM /A1 ]Qzm|P/L(>+c+gpyfulF2F75REoS Z.'5N e<&dqv97q[e"瓽ի5P63g̉uy%ʉ ~iiD믮pw䖥~/}v4rRv5PFVY4#bb=J+LE.PpChm4P'"LX3C'bL^Jd #7VM㋞=_zupAL|}=8ydallv%S0Kj/X^;1h5i${2'0_Q\j$it K;Vtًz_,Ҙ-e"6ʣ{ NX8*MhgʼzJ_G1a] 'pZ^!] +a@bH {a6awH~](&"7+s۪[F>8^ꡇ]TtA^YBEr-oIRr;)52?6HJ9 |P8|MJ3ݸs.,ܯ lJc$KlPe~ )9[WB^Pwh:sHs D4Zr Q`[OuCeg ]՝q iXbR'R .݂[R \E`ǔٲ0R8*`&;eb,nt0L _+mlyo,b-I(mefUcQ`ȌCX!,1>`-B<$5Tᘟ?P`; lL71m!hI,cڛ t;j䤩jñx[GW|-__]8K$y|6l ( ]î P+<2jHT@IDkq(}[}Wx3L+V8jgKN>^iqʌ? BR1ƛ{1]dxy0\b|#sQHohbNӘյcQjGZא {WQv*ND-lX, J&HlFaN'Me>dޡ:F6K%;hFm/QZq7_㈅WdW-I&m* 0D6kDXȒxƬG-eKXCCMX pWj,]5fBZkXDYs◔^>9pP^|D 3ʉiN4Г)#\gtZEYk{yrm ^W@C)}|-Ha XL$Jv6Zyc80zt !ZęZBTY.Gn~/*cFؐ98:B560̸;c*FC4jEG=Gˉʉ{4(${xLKb=$[Y-פ&P*ZzWD}}QnLL.t1m Ȇf<ϷY\R?BLd[@yE<_)qdx'ͺelٿqU?$܊j9sw(YfPx,,\8IFm{D!Y}d_A 쀉$,᭔>Ӆ({0ϝ(v MufIiUW.K0/ qUw H"q8*pѭrRʋM#zBjxX& iZ|ߖM m"{I84MPM\֯)`B-H,*rJVMϑJtY5=YsP!o]q_e = ӛc+I8iQUKPz uؘ&lzJm71z_xcًaXƈ+ލܦ?EIVO-% "QzUc{i%Z}@W-`58}sI)˜Q%q!*$ Db y/ "<8+>no#ИWL3y+R"@4!&Ɉ*$w@Iwm d^.zmɛXd<$KI~צ>;L-8LӇ|I뷆Mvr3B/ŔV\7dgn&Ѣ`8= }NчCul&AN8c[< Wv&j=k[S+'(96r&j+HzR[R^C Y}gJjƵ8b%>ߗ3K9c8gN5΢.ߴIHEKjj'Գ0Ot(ڏ)BYuͪncMns=G@S+i?@ȯ)`kgHuoMEq3BLlk8娫L/|坨ޑwi-6kHA/f޲t1Y-"9y6>/U(K\c%B.mlV*ugŞM;W%UMP 8.BQuSo2Dcz]% 6"mBIy13nE(?(k"*BhhD8v䪗 a|]vj"> BϷ}d%c4qB6 Z5ɮY"%SA. P{u,[Zc xMnQ-DI5)_qS |2E6%k\e@"%&F zDw*aB >B|Q~IO_G//K(@/cz 2vshD)Z(-*^Jys%' [J;.ʸ`pKPzz^^CHT%Gr1 .gYI KMbfZ*HxJHru 6Pjau{0fZ4jXiנf/R[Ҙ&'2'ꀚA>CUVz!&kpؚҺM }2EGՃ޻=Z `~/R 5l4RS[k`vW'r< tŁk>~F6a B{@KL=m+λD؁nmvEQtk,t꓎<0utګq@(iޣh6FithFapctfn'UpHf\/MxtuKD Ϗ\̿21S "q\ud^}ӧ1ìn** ~Pq֞Ǒ}(|vR1pGKf/_fSoSY]6 -v.p隽oS^\X·C[Q<`B:ùK4S)6_ۅ:bfaoҨ*'maPqrEٯqϑvja^0-(rg=[Sr1>wMIJlRY*31 je+Wz_a5 [5Do!¶< U ,H9'd><w0-un}؋/R ]Ѐg!J;TJdVϳxӆV0$iʛ"jTCEdz3B{OkW#1q~~;HpÎ-Ji kfȢKZ%}?:^Oɿ rfC@ky.]\{jE? J1)~ .jnd%rҔdg-Of{&7.FW *1:) Wpcƃ.N`ѺO@`?w1 LSxxZ>q%ݩj{mQfi`21e#=[p4+ Hun:UezrvV|3E&S6/#?l7;Nȸq>jQUl{hw>8(gCXֺyW|+ž%^2ޤ,Xr%x.a2!P ۹V= zQ Ct3(pzᙟBd5 85]I[ק %?e E nf&niR=aVZ o~N =;ގ rI5E|惥fr tdމF`J#$)(40rP^[7>PBW6@jzfC4<,+ÓX\n7*/偿/̟͓Po5 ̶<  d; '/ewAђ=6/ew֡6k1eޮq_>LHC~wk7.ۢ{DDGwCʦ>q5e$h+ʋTY)`}yD:ZsnVwGn#De?`,J4`K=A"a-Ň]jU\W>_>MnQ" SOR wZ8#QRQPߤ{ ]?@LT—MHVl)y Eo. 7$R cPWșM1ΌWٜv$aiHߢ;ޞ<MxꝌ5gôDa1ej?x1+9W+"P{SXhCPe.OUW*K0ҩ ofgx:zDh3 vFFղ`5c\g88@/}B@C(0qj2q,>sj9(1kEm ڝKQj+>ouBt0kmJPO 2?1'cULxӷ ^_Mŏ{Қ'SIN| /S諫zv̼l:P3m'j_u Pnj1lY (_`AP\^ne7-pm$= )(7)nhVjɤ8Bl2Ue{tr0xqi ó+uTy pXxZ":w߰}5@kdU3k5 n5iۛh˗(_:ҕMA"ciIG4/CCM3E05v+_)aкW{?,vjqo/?!go#ErUB(q/*eRԌB+^/jx&9mحo@Ncw'Cd#0 F}5y(IG r}K|,4h[1O}o6[ /5Fh' 'DN>pzȂIT` KF䮆 =mЋV2xMq(_m>hňS@,B )Sgr]ВE}4{qkOC]kMx18~"io/>VdML:Za+']3앴-Z6cC}n뺝zpi拭 3 zO\86X3CďG9s_vHgHհkoUU|fݐkC?C[8"\~TQT< G d~ AL3luL\x8ִ ~ZV _aOhm0(imu(fg_1yC=qjzyrW삗Ól|~?Ыpξ3o[N\=H2q)9³yގd$z%{_dA cS5 X"҅p˚7Xv{+ q]0 8tRGS-V F³ҍV$AVoTģF ſ}U;(RxL:[Kb3ܺ_f5đn Ǡ-̫§"mŖD`hڽג9)TY&˥sqޛEfm/їF  IK d4NLLQIe=#RM`?J';Ag, y—0!{6 AJ1YLl7s1jҿcT;ȉDXћoy(2 }w&'YXp{s}?ş)QMKeg Hn x~m%k0dճv+A1}-ŅF[pZlPx\I#z; k9=c`8S@1;v-bG\M]j/Mp.7ZNƬ,&7lUޱl7| Qab趋YdYu8'H{RL$!/-/*9u YÆsWyXltx$lUG-%謮1F6`{\#ӾsBoSY,QaJ EG"_7,A }TdDv9WOh?N,N+ٺm߻3O(>aъ㳨EDYg=cBee'sتj>@|^xbюH-\(v>|a4&$C4;8^xI`rBW hi@ f@jCP{sڸ=΄EYA9|2AGj\jDP\S*pbK$!ԣ7:u`JuȧJ' 4OS\e$ *t<>x5l^c%[ތ / gr[x$%gNGg{h σ[/VfV`ea23IV Ww F!Aeܴtug:ґ[uh5n`"Q{ӊYˬes`ߕtZ]0k8Lc)>"]]Cc[Ǫ18b>|RFg D^Ҙ򣐉:\yd!w규L*V˓?F۹ѷx_8bp&XfXedrxLɥu@բY:˭t`a7:հUT=k{3X` T=` &Z=!R"{c;D)/|ұ^{;-DYmÍFny6^T#ӷFɔLx+eWQip AuxD ߠif:em {lg}Ɉ)!,j&{V݋8ex ƤQQx!>׿{ ^czϞ )a' 2ӛ}85z-ΠD7?eD>js= Ɂ֊mi's-6weOc[Ht^2h}v})oi)Ϳ}vMJ%^Ē<*.?B&08vF/Aۛ6$9a-֔mZzԂVFXX/9UigՃ^.ij|ΥYp1 48r#9:є3rEBMc|ET>0h%^4pk%]J[#wT.%w/&~}kLW~M>bX|YعGB [~̑ - و1]}a(c7G AJ KH1I?+SIls*l(٤0-*z:0-H6ˆޗq]Ht=ލ%rߙ2ށdd>h]I|:kciXpuISc;|xT{{:(8au! kbhb,x׳QwZ_ȿs@kpqyƘ`;5Kt I6RA?JPAeGZ+A^x#J??I1 X|XL0mPf#IgoU\݁N6+#2~q_1xT &>Yaio6srVW⡯yKMqnKW'ib_-mxc $-1Zރt1k^+7.92EȜ{xlsJ[jд}(mlU:ex^}|1(x:?^ԟ.R-?\ǝ][x`WPi۬NI%`BgM;J|VQĔ"ga&_pwB|PebR^S5ZSvFX / hS- IϽM4riZ"*b,}\ub#[+ w6`SaX{,~P]Ϭ¬Vь}rf FPԨFc0&ŀ$L62l;se;iqҹ&ˀqhZ>1rIC_BDZV7B[~:赼=d8җu<+_H5WYG ,_#^#Ûlսx9>#uFZD}^vhuϷ-Ho(㫍Y'TM@yBZ@#tϧ4п+HA͍>vNǦZ=?D 1n3In=mM}H6!/ʠECfj*E;T$ -(!\I<{hJ`=_s4L͒pD_]pgDhy _Q^)V.dу o>$ң]~ދ40w; :–!M;ODJD'N.f1"y}H}J֍\]tNL9 c瑫tj 6F3ckzu4yr=UtZ809ƴۅƣ|ZOuekm;ڭw] hԁWHġe^ߘ͇:@`(A Hh Y]Id5AcKcn0q&xyp` y/-yVJ~;@ObWX̉f=z2& uɓG6u,1AH*_7sZGTW;Iզ%)a~kL,gvy  XpQ]0-ADŝ1 7W#z،}pr0kݣ?޽'R=Ȩ-idSEqPZUe݊M?w@5"dҥ%A,[ΞPTF~ۃY9xL|17HaHSH\?GnQ~?ʺs3 RTri v)ly2M2S"Q%h1p~i|xxH1 <,xo0Ʌw ǎ˲\l VzB0D]SS&C3e2rpo]ƊtEsDCO`JKAZiS`?y3S%G@`Y+5N{0C*QjW;6%LY 3 > LʗTdž]@EzKQ8hn"84qXt` '7tǛ(PDjWuヅĪ:!An i92[Da Q KV^`tQ ΌwÎԃX(qˣhV:àG'8ȷZq" })hgQmJ^Dž8xc)`!ƹYњ{xwNK=!$%!efS7տ&"qm%ne& pLSfZA#.rް).1eX/Fsv.hN% &bOĎwJ0_,c3Qo j]-APfϽq}|P:㔷#>YfaAW8ž;Z3S[S ay4J (T12`gN%\쎈W-B|MT*gpşsG<=N95R0t1 +tZg9J9Rg-BFG#RYLZ=u ŗ55k2̇_a'Yc+@ACX 0GCDd%NsXiAiI;Ī&_AQ'uidDE_֑M3rO<\-bnHd ѯm'IkzպS.I~ \իQˀ!З< puZOTh.Л 3+ĵ/fMcosT\*tlLov9 'mƊ`CYm'%3u*]>AigOnN @˻'B8ʰ`DQbmU{ձt%$ e/Uc[I4= n1s>-kz~vb ֱl8V>gKfBƾxZx9`AlLDO/~Z:YB:zyU@V~>"- gmȭdwTƣPY:G1t`^+Ts/&ʿ endstream endobj 98 0 obj << /Length1 727 /Length2 15452 /Length3 0 /Length 16048 /Filter /FlateDecode >> stream xmcp-;ضqضm6;m6>{n?csYlt \JbB,, tpGs#P `L%dd"db``#%u775s$0wPܞPȌYމ"% @hbn !)'NH!.B( 8ZʘJB{B8F6@cqr73b&6քʂbrʄ„@cBʁ\F,YڻǢcd$467r$4$Ć?ac'!?B4$4v3RX:9 em@B!+cBQWG`_/ $ Ch ;;#?R;iEϽOw;zAU% 5Ӣ@#cs)?/w/khoJ@%aQBB6ĽllL^/FNeg^'`dch\-Z0SI=3B`fwpskϐwb[ p4>IRDRn]Z{mm$qv똶j%#ZʮU1Re+ve(NCأB1 ʿ2] A&3/={0h8[g ~Zaժg )U3½sPp娯mӑgCڷGKTLVO pY`䢩/سQ[Q-XZBxLSɿW™>޴CYD8yb2v7+Zv-|u[>׿oC~hg^]%C]H.C†&SwO $\)!eujHl1;"p͐)fBJV3:>%c~0i@ṛ~1F*] ܸ:,"7qBWa^J^5ݶ9 /̋h0# -ɟ@vbLX(C?]'):ެi_ˆ!Loh#_Qsnulk*ʼ`fޟEϝ$RHcV6}9qG&<)#QSt,l-yAPq* ƘnX8-#18# }"Նt:;qqNzkUsRE3"l2ـ ƁP ʧUb)Ў^I ]E<.HzE552x)Wc˿m^|l#I>){i[Y˄^Q9gL۸g3 3 &ydi ?:ں}yw`q+;E$+RAh@ ^Utp9zΈSZ cqh]ZZQ0$hwT+3U\78}-hIΗ*xK=񓺬iԶGb/0^wGMBӕ&'p6KBQMWg-Y%g[ś; L[gw9= f>0]k$<ޥ?U@K 6-3>$yZ1tJǔ2E}O|ڞRc<;S@C ޡ8=-!*-Ak1xκNbd#e=a $8 nƾ`1jW͌(rߣZY^3ɇɁ5yK<;aDvAx*,LKaYMRFqb2ߠFuU;,i4z`VR8N\kEen(ښgBf6Ɖ_rj_ӆ~w?/?6=3+8yj0 >"3b~mקٝ g[7) W|ao2+'"LĎ2J$mzjq4PLiۺ3^CRZ‫jLW;/Fh(cZ1|޶ %?(ڮiK9%?'~lܚ́lOտnӝvн =⌡7ց|N mm4Y#^C)j{+ߔB,Vi<.dee/A-@OTGz/!Dm].``%}n\z.ͫ)e*' \ƫ>9 ɋB}_NUZ/1HE<T}ǕQp8j+C`-oU';Yz.wȤE䇫gB8{ŕDHbypWfC:;иz.Wۥ̸;nkXQӞa3Fv䱶A.7ۭ+_Vi>UDےu:q@7:s׶rpڱEhhOă긠;B̖}*hƿ8[Ŝi%ANݰǡ6o&'z<|GZ#UZ28ͳL5Ԏ/rkxs$Ma*}^fum.ƃ({ CV6OpNikOgemT0/&uPMeͬ[9b2lHb>$8"ɥC̻/pX} 'TAKEbD,`թ=%%V(&C9WE?(} 5y.1ϪMrU#zc^x:ri0FIXŽ!i:Zz-]ôG8˿%mOPFf'J zdͩݳ)Fg[+((!<;Nko90iy!JJH3O@4r^#u7BnJ[J1?aY;U2RA6㍶TCRw,5ztH% :Yr2Tl&\iy}Ar\ư 6tMEgBY.4#x)5߯=O:ݖy&{xqC]]j[Y('J Q^3OGӓ|+yw5TWj|b{NC9KWYl:ۙbk}he}x攣\ճkev;c^0Ox<7]^Yp}$`4 gZpv;S章햠'>k`ui]hUZX ZMq]-d">-S7d3yP6l"Ϟ@GO!ۺaEq:>~^~ h8.RdgTHp/C2;=`p1< 1T <ďIF m1k{|1WTyK\((a!]?6 R M( Qk:&jcQcr> ׸?V]ϰ{fCkg3`PА6Q]VΨN0m {ǡ (ta 5N^%hqq EJݦ)adIDJX gT(O1rql +I2{5f .|/4B%3Xί/N ?(_J:y$́aseqo U!G¿B>?tM)kb|Nk0l0l]ax)h ԔHE L6;3(L=Y8($" .[~JZV3쬸TL}vbVxq?ҐYԹx-F9xpKwsI$aEgR;rLT"T^3bFxB }B,K/zIE:z˵9, I PA -Q(gG5' +؉DR?uG`dN(=- 9ep=gY`)<4ATiqWdBFdnsv$b(\$Љ a$SHDJW5S/ٟ6MTߵiƈ;Ak6f bhc0Ewa 4.$7 [:}DI~cGzoNٛu6Eu[#ZܥK16hu)rg_]Gy><J? tt@k 5wKHAr]1aX?hm)]{߽9⡛ LTc5zgv{8er'෦I հs*w JU@_KcĉwXC,R{xZ27¢zRg'tƳDT)zDtz7ϒ˓?cR)YB5Ls0z NBMI_2aڈrfV/:+I&l"DѠȝ>`8?'Q!)ȜztّU+ F=A^ncџWp1YS۳{؀}&hL9u:l\ܣ"(i^JW=%.CEj:硡>oS8G8K#gK{.d=]=_`,Ӊo":E@)e?ŠˉJa_ZCk3П\£PgQ".D@JiphdGc]K -=M1 .V-~BS@RW{a d Zw7Lj%V)ع2qIKix9`f:\J`(Ȕm)c 6\ȳۯ, G"? 2N'!&KbқHE]sr"w)ԣ:^ѓEes_r!m(]Vs_淌sV\-_l0_ԏXۥVO fMz+A>KZ$5v++./.Z!>JoR Ip]{=w,\lK*&-EBaq|sm S*.X&eb`щfi)2ovr-"hFl6b,r9ez˜[4q?#cMG3k;ݴl셞l`Ӗq!ʄlHv<ݴb:\(<$]GKfdX֡H8w%|8z 9:*]-S+PX "dˉ%x&Kd!u.RtE!8-87]n'%J;SKQaƏFo}ej7P>1 ަ~ӕ)l WSݵhD 5HSޤgq0et.3(rvh2h \ǓYgkC'rGpDvsc.|#[%-Sh6^Bo6*< N1؇#eZ /v=bEcKXG4DۚR=A d*q'm3u~I6#dm=J^Mm̭+tcɯnX"wTJ-4D^qvX@`t?nbrZAݟ uq}VdMcg79Z)h](0iQƷÀI 1?x8K`DZ,~em# !C | -7"~oGM=8-{43}VWk(i)ESaf1-̼>W>b m5݀{xYDr,oieE_Ls"0Sq"c, kٹ>M)s;^a<7]xqY0^bG+=^:e{IU&͙+]{c(8<]@.Vowǁxr8GJZ׳XQȯIa&4W=IфBS*?`'Q@=?芓zÀ p55Gy) ?(}Idg -OkUUT)*27݉f'4eCj6%?V1% T2|e cKN3^ѐ7\I3% KpLҵ"HٻE[IE~3œ6 >\ҟGb3L>~BOƀtu78AR騻ŌFeofUArq$^S'A`Y&}8H]yH\/KPt`AK/eþCEہX&OHA6cQs[FͶd1]ZU.0)oJ7幅0'ĩ=mNQBfc;*VUU,7[ؾFl@~_Ft%pYHeobm}‚OvEoJ ~% a#ѝ5ٱr5."T~4. {XS/c%ϸQ-ف֢$[_5du?\𝝋^Qb!2j')x;ed G/92ŐX?٢p4xڳ4ofo%J[@F شy|f1p J;0{M:I J|$E*N?6YRn?48Zg\#z0w$l1_iF%5p}'PF҉5D34골R=حJb`1 "`{տ[Z^ N``Cup͏AuuTKUu>-bL!._/閴w5^\]<Hn,:LaC0MrR`E/UN܇ל <.M2h4ˌAi 10`/a}%A h@s7V+::Det2׉1 QlA`4zHĢH&@$ AN6Eq="5.o&c~ N\ƨY޿H-ɐq_KsQ0@;`xTgK[L刕dMt;HVQ!R246{DvNR$f7w%aаpUنF]0ڧpXkB*!#2g+^qb l"חJ1kNVcZVǨeX1a3MlKR(fhݟZm(^?,S{MFh!Ejx+Mڊ9+A6?Yp,>޲PRFCp v12l~_L{JjIm rᇒ1LGH+2O# -\|\qIbnr, Vx`^qSԮ–2Z/&"˓rH耈溜ib $ ioiڇ=i#Tt(ƍ.~m' k57XHAՒݥCNS1Eu[Lij *kdC~dT֧.щ{l#Y~-h, Ҷ91YW 4q9{yI9sojn(xn.BotX~ԧDLJ:!yKDRMuV{8Wnɧ,X({5ܿ߬whgswwS$ Bn2JchaTP9N.]2[ad-FRq;N^YД]r`+%o8;NGv(rΫ$L Ц1-^P{J]lF "ICTN ";t|c]qbe" e Tv/.""۱)wG}0CH4@YY $d&YN>̡G[ ny'~$\iXf%AyCM`n {?O"-^ ڈ?LDo{dA4S[7Zћ տnOG:kVM*]Qt1L s׷W`(˻5ĵ_E0q?&d2}}bENJ WD`Яa#jP,Qqi6HڦLҸ; !S̫ KHD* **z:sL:B^#cp;տJF+ri/z6_ݰC1[?#ȣW X]M)])qu;d)eZȿ}-.PI4%k+4̣Oek}~4?=ZK󡞝NoL-@֍DmV25)us+8m O] T72)o0L?4zVwSXV;0a0ؙkC!EbKmRRU%29D =QXvN La;4꿻$$v *rIK LxW5E0^&ZɈ*Ӿ.pW;Jq;q݁d%Rдlی50:uU:`Ոrtn8^/\ v?m–p))o4fN(ϴPEUzÛ7*/:0k#Wkb 4a;Nq2$@[ wna솟w ӉT]`'$J9 wh3֑Ê*pxK֌hNx+I1}5#&̋>a3_e$ 6*t̙+tJX^`STNwnIkh8칓LPA6=x7m?_Dg绰\xSG!PѾB])#bpqWhqT:̿~PRjHQe^|𸲤e&E0  b #NXj:RxݠX#} !%ޕsDx\,3Y? . 96]jL `+OH^aEȸ.!na5ܾUK`: @IM"-ȍLA.N:ڇ7U W@KA<,)OQ73 &Y!$0(^5{.Z昿S%ibB;ւ9ٝO yzkD/(W!鹔 LȣTq\)f~7[ ]'wg. 07j.Ԩg2 5ݾp77DQY%X)W3RfRl/-?z+R齾/{DWZ,ݡCnwU sEjƳ!Hz*Gż`7"kyA7qpnPط{@Y 0*qڊъzϹH ~kfWo?k" Jśiіʦ5OqC@pYE]9!x'.lp{4nPVo :$^xv.42ǵWV;5S([-&V*q&OIڭnI†,s*7oFvcpڇ!+Տ fFeJI}19? sAkXVCe$= OrB>o"^e `O;d b"RdŞH9[Yx>ׅ! `tr k[@..mV~}%Dl :k_H8UgGYݍȆ#W )c<4v:2RfpQ5KT|T޶f#Y1?'y8 SpOnG{Uu"Vj$ (n. $!BǑRXV,8Q+s[JQ4&âDk drHW{&kr(LԨ!5A{_j? *Ӟ8~.R%>KQl$c?Wٰ+t\ J"s&ȝq).Ib4;YjSIa!6LqAk mȿz#Bm(ɛ&KM=TӷhzWڶg!#uJ}!:'8|hC+t`cd؈d4#֝P"O}z%IOCO"^9"\},y?*]0_ smW%Pd9tߘζm.ݡtMLpW\N%&UːՏbm&~ѹ4 JyO)ϱA1xAys.ҷetgdgWJ"c1}?ⰁZPMò '큠[6ʹշps-ؿ{'+Pg[sɉ?fcD"Z~*ҎnǓ!~k3~y79ȉP~I\)Lkruk\ tYCS N)BY^]BxI{|r/h2F=LeӳeS= 6z뻚վ)>@zvTOWE |}[%X%z K?C 5*XR&f̥+ˀ#gΩ-2nDtxcո6 ΄; |q/W`{K[c[5OrYuomDU D!tgmQhq. o޳@ 2gM^u_x S̒ y=yE`qwuE.8ájmy봣PJ&k^b\ޟ#y}Mhi? -cr'2'T;›cSdb P4xzRH`t}U.Ro%MMzi]ѮuM7 xj`~ܼD!&?֣G#6(xJp[{AU+xn6.>րJ"v?Sa?Hkނw[b=`YP(kN )@y'5bT /_sy&|F*'2]ceen+PPVdW6B8zX+o+2$\?hr.~ێ;l"s߈undYEL&Ɲ2Mĸbgv9q@s6^ŝ>MT{YRI#*<d,GHڛ1 hE"\1$\|M;T&-PB̐ˤUPL.V[luMOdcNgM="j!0f ^0Ggg~ښ>s}Hh4f^> stream xmsp&6 6m۶m;c۶mƶm;;>端柞FtM  -='@ITP@OCB"41p6p2L Fzz; @nDo%@Ps02p8:yi R218L-MBrbr1Y  lhma02u4l-ɑl]LN3dDDd"BtB[c?Nx:j%kJkGa`[9 M,laś?jcg1|8lm-k`c wv2dMA;kcxXX_ '5A,E-L-W3+I\low::YAa uY"Fvf%5WozOO^ hТWk7sxAc[!l.nuCPEoEHM */7ܵ6yU'a']WH>E ̼h{}ř~ƇB+_߿m3B2T“Y)!Ӏ/{˒v,zkbƤ,+S/,JP_-t+)%[䑜;1?(r.0 if)>~ϗ˷.ݮ.NLQ- 2B{|vHx?/a#;Ħ @|wñ$.zHZC@elrCJc5&/(?Ej=^%Kף%X@{*S!kԐL;74N% ,5G3%f"Wb=M![<_L!t0IJ2w$Mvt"I5)R^zũJR˜wι4+~}NWo&p ݳNXw'KѯԽ:'ل(R(9'j4k,W {Nt7$x%fej1C#Ir*dꪁ aMph d=H!#yů&EY"cqҮW79Nß8.#Mi^$(VONV;e4?:ȩSDsfDTlXqҨBdi^ -(F*TqOWlU6\{qc"j 7UY5^v Z]rw Nݫ^IbtL0g8^ośjx&ݮ,L aJiMEKm&;AL )1QtBg)v\  P!)b}Y{^:ʯɵ+ !kx6%D;s^`5ԛ0jeyla%<.[+oUO"ԫVquT <;vׇ5.SX*w*{aKe޽AdӭBy՜"+ӑlQ;lҷ2!e=^͟3{Rl2w{>* nb+8#_r=rg19P%1RR!P:l'0F[qmMH)-/Ml3=,u-rC.sPۡ!X~k !оl{A8m9@BhzTFUsYg1 <5ҝLޤ1K71k+jgL냺J[~L[yc;h,jXE\Ze~o 7}d=, >0) "zl"ۧ6KZ dD4޺Ln|M>;w5Kd' h}D}0g)+Lq9-b`fuQuJ$P.^ł0i6Xy@>̥ՅY[U~*$=۲QoUR*BMr%-j^I^O^Ǒ#ԁ{ܠS2aSCl9?MO]}G$Phz n$hW3cKɝ}Ւ➺|?QO{pF8[ASh_1,>)UupH-"}ҏWv ## lx8(tOHGT17L&B6{8 bʰQwc"S=D$U0>obMIv[GĤ3?70r6~Wp}Y.F1Sw5 n 7ӵ(r7<O8FlG|2_r ~"dE&pM-Ih(Ep_# 9dOG0zh( Tm/v%ct<{%f(_2qՑutb-(Сj'{^}6%ªZ>dk[(RGٓ1vbjOۦnˊ*v!M[NK0h/zo BGiՐGLW{lPrS.p¬o5El='ZTPҧXH:(M 6lHژWkKg{~Q{2aʕ$-*^N ڂ`>|9(rG#6>$z4Z0F(f ܎A$/o>U0Nqkdu;$u.E8wUUHKYKBO3KGYȢ_3 9y<^f (_BZ\Qhq^1A9 eq`-E%5iɿ2/1%y2~pȧ Stsi gv^> TWg6|[Y{N]>W1.\-uSLPy')pzvs 8wQ%< !LȔX)RZPmazwcZyH~S~ # [s^\Nj1Ox=k0?n/E=Hzs0HU#U^3cǮy+U3 Xǩ8,1QoQ6G gaN)s"Vl*yf0*#cXÚ T4(_ 8.67X(( R$ZҎ//j6DȧOabݸd4&ieC霴%lSoˋ w+>Bߴ38Š*7=0a}`i f (=2@7ca;rGu Fd ѵ//g|2O:^_Gj3n?(gHdxѕ֍;Je RYѬNnY6L u|֦tP\r5J7 vH9{זEW#WP.kkp.&W̙?a-gwKNAf$zQV3~fDO9yUxg+jC0)]>He-Ls,`q9芫hv\,ͳr¬yE{7(c,Jݯ]DhtøWkzNiĔMyWi&b)?T\0g#n^)o⋕V|)jXh@\_!QF0t+W-vQ7`݀7ܞ2dn IQTPސ)`X  HU]IxPC0Px3pF*99-0LVyzVz&+rẹ+clv#ʄEVbT;pkH5,ja.|7>P+l^{Մ=G^<w䬐k"ctD_ggS<+ XN=攠1uWnXT=T kfU'.n^\`OX 7YMsfROI& 5;ˍub?U$66 vʸ;ߛH cnGm5wEs~h5,Cb.d -IR{o^ m„%ySR)e! NO{n(BQ^]aXiC_V3 sk_rmzr *x9c<b}^D*_]_+L/#pmYJc=0ϧ+KK5oqnSO.;vM ƭN%P w6K,PnaZ $U`.3ݐar[QVM?ekMMOiR;gXϑ('1gL' VYH PϙH[k8rٕXoÅ΢daEQY-i9f⽤+!Oc׆.(3pjжj_%5ܮg>25WvN;3uJc fJehoԪsu=w0qJnުLCA*IZA#F߁撗!Z.=Cɍ@'pYZņKgFgB;鳃sQYF,Dmcڬkt2;r꯵+*?F10V'չhr.~~QX|n /eX S?iqDV:59L=lj#'f1W^JCkw<؞;XFRĢs>P瞏rU~C\q֨HsϣDBR(dL9J̔-5J/8"yOcgBU\#Wb9[8| y;\NWD ' dtM3<'ouvv+]AFؿIQ'vFlDtܗ_װ&Ҋ`-f7[-<yԧNv,V+ʔq=8StgM;)Z>/M-l4L.D#o=gvݼ { Q/ngM8#g[7i&@#͝F5Ahˊ^*(^of~vI#L5Cp \i?ɑ{ZK`^P!Jܭ_VM {m r'ع4VԩVʼ̜RaЊ7mj`6oEC1 vscI36;+]ηJ3T6~V>u|n%~r"n6,v%r<Z1LT_+0?\_^2xM¬cuGY9)e?r;n3Lqpn3H,>6$Q,NR$%Uc}} ]`R=TYP?wzUk4[=8=džO'j:ϞyRB CzbЈ+[([Kgz@U z.xu̧R]0%+0.o.]zZZSn#۟34q 2G|򉷿5LښRk&~rDsrAvQAFNt9|tQ35`ȣ/QkF,O'e-;t`1Ys?~jh[yr[ª_uʗIyG3' 1|Ʒ>.K =_`%7 ;e$ LLSZޛ^iˆŘUĉHQdGnZrX-0aZVkfmyGXSMZ 3U P -!9uԧ7E#>9U #m~\# ZRwMČV{ƥy[T7G9cSPgfm|:_Im<5k: It՜@iŠud)Q84iu:|a>2Ѹ +1LĆӼ"X+uI L[Z5T%DtRI쳧IihM, ?Дt=Kį|dE8@p LF?QSlSt1/&hF nYшD|cu:A}[ט$D3K9U2?bg]x[ީڸc6si@O~u ]uד(l4JT6>7KϽ"7 &*H}4wbu/!:WT7 _B J(=堏%=yV<^QDi]|MvfBR8x o\F{(fc> E^Ca6Nl) 2)$rudJ/|[qe%@`H&65I%AQM S>NYhŽ~viP7)#zӊKF-}bxƐy-ak d%8˧݉`Wrdϼ[&% T8i1bEw $Fgǜմ;eI;l_˹1&,ChuxSc6c2;X+s.)IXr k6O&c\x[e%Læa$[ fֹ б}Ws, :)H.W |ҼӤ`X`=q`dQ1RE ̤58[x]+0%*1 {AN!Y D5?Lɶ@n53MJ{84XBt+[#t+n':T-+M۞ QHg$Mrkd gISGT@Gю;!ruZV) 4dL;a068~!a2x;7cBlyN `;U7v2f<̳I F :r3fQW͒+A.R|:@%z{/j9wc.Md!з)5?:Q7V@/`jhɦ^,+Yڔ("v(*AvE6Ћ f~L}cjn~a|\u4xcBv‚blvEh䂽@n}Kp2& Xm_TH6BrQ҄a{x~Xse%)^#knrixLu jXAkN|0T]^P10x\ 7n/v˚unЁbq汅Lד;?D>,w|ӾrPA8}ùw:ߩW4Ȼϱ4@ZdtQaߥqvgp~ j冾q;E]kb#SBr:JǝGbzqn#?C[I`HF^[l0J܉ @>lHf-Co4Eq/*AH"AyKR(^Ĝ;SjytJ .Ⱥ2<zQe?L&Hܜ \ !śq CHξ>c.YF"VZOYCx_ QH1NZqJ*>Ͻk#$:EVtv0BR̎a9D44窱Vhc!@̂o Tƅre,IN/wN}Q=,%C„r[LZRy.sp 3M)pT/0ڴ[G`x!/;Ó.N{t'*WV)E'[ sq_*'Y}p ą-XYg\iMHe+Fu{:p6P!79q1o6*)jĘѷW5B[z aϹe<( /ܕg9)D{"m(w-EoKͬb]sIA֑.;Uw6(cha5%""}TIFF^5 ƺT_1d?-Y=:=COGK$;;"\&\E7ja|gXJ_=~6HEŁ45v mDOʉd6ᶞl,;g=\ݣ8!0m!Ksn iAo@k, |K>M$ R[WpCFN\IrD (U2dq-- Hek%#%gƤl)/\Ht#LzF;(,A):Nax *o<2@jZnixΔiqIXbTY gw"$o :掏!![Pgw/ãS};v| d9]I1 `U^{+ځ "5Q.ðyjD7߼?HR ®޳o!܋TӅʬ %:$/k:A;>Am#T ,d F⸞B&!  *8}pE9[33i?ovIKcD]\y4y~|\eڰ>_AU16g=3F2D"KfNZqrIl#_k\?;ѾI掫 殸]]`ي/DW` Qd/1)Bt4L[IА>aH>ɵR3cD;Ig``M0m`ߧB |n!*Y.Ž^7AIӘKPνqgOU,Plk5}ݏM!ȹ_EDzB, `bÉXKH.>+mA- ZW{Yw! ~3p?kRnyR]:w uiNkq9AdR N@qP1uJ /2 #A =d>X8{LP w[ۏ +"#j%=5<|4XIt4^:JKxfȼ(qglJvG#xaXR8_Oz 7穬?PF#wN H|XSt̆q|TExk~'Q{  E||>/et~R#]" 25ż΂::V'xKIIĆ\Z 9*8 Y{ۤ]̟ϑ{WC%Y&5oE[6t 8Y]ߊ@NGop@=]7/.C7D2$7Hj^b$7́*5#<#eƝ6+;ltİ,bƫUpйIwb (B|=~ȓ7q%Y]lf!?ٞG&oC&eWvXϐ3-9qmѫkj zZD-yUc9#CV `$a׉E*#R1jdHiREFpc{3CH74T\3o3s]:gW*[YFEBmږߒKx ,;%P; g{ %(BZgf0Z; ߫l,E->gY$._Z2CE)sGYFx)̘uK 4LB~O]bBtEVM0N+;.|yN]OMe-d_K˘w댿%<Ci:};`?ߑq6b]xmi'^L~{sD{%PlK~[zk5;ʿ/ޮEnCo "Un+" ^jYqhOM%@P˔{ g;ÒlE~QnB{S0Ƕ?~ Cq!VqG2c#/ ݓևau>у4G')n }eN9Pu[F?*P1 9W;hukPɴV h@T ߃ iF1٣`'0ny*5ۙ?u*w8ERkQfBB*Ö ]-a>W;[Vtg/ ⱞRZl&7E"4̬i[m|͈$|g-^l 7ɛ`_j [hY=73H 惕,Xssy6t: %RrUXнjr|X4QǡyˌVH-E +K3~ЭcIQ~Q~-ňrLգSf3lᑡi P_=;?`W&xj|q4:4ϽpaN@[71f%!i,(z$2(qFe>})vu⓲lֻUi ȑa$n'}PWe" $HI|JH-Т%\2/ +O=11Ӆیᔞk4-qw.TD-n\&pPtA91ȿeaJjH PBT?qo0n]X&UH#˓G} c)K!qZ/ŵ4؉fjYH̀m! $J+[VZ3J!N76V˻uH ]wFr j'*>J""Y)YMfєuAoÖﱝ}8*CK2][p_rl-`lt`v"7F8{u0*fSX@g.O1 J3i"`xxO G2rڦ̗2"RGt7E{!ןz/#d`{LUܺ܃ǐ[C(I !du~E^ ] "?8Qv7W/eq(RTN(U(=޸_ /) NO'A7E^_sɈzT̴[ͭ}_aZSvG/jlM=~L#O.>z6zZgC(sg & >GY]VF9E=\ݶL"\$)X @ 7Өڬoߺһ+Һ i+^_.]@ZpO,x1)e0 5xqI%mψU̿C 1>1bb"DPj Q+dĜۊc$J'> stream xln]-Zʶm۶mۮ:e2Nٶml[}sL5Xsdm2"q{WU/G3&:&zFn3##L^Ռaf P1s010D,,]&TmL쬜V&^wwwAw7zg7~Tfs+[3RB^ afold Pt32ZٻQq&VBofή3wvȉ +ȫDTEFY]]A8;Z,[YuE02qYX0K7){s¦nMC~4v73P899L͜vFEYzB-Q?!+q+O3SE+67G5/rfVnv7Gv,O^AFHJVZﴘ@-MwZ?_2&\<:=/JXӇ @`b5qsv6w_߃dfif`j^/V4WN37cF:sl5dijpxs?d̷w+uLl`w<9KZBRa_TV mg"vu皹H=Ś{^#Y)ְi⣽J7_j9˰kcu=2l 66ݥv[{w@QXjk{D"~Tg|jC':î hqw6.ag +4Hr9N ‰UtpC !(s ʩDtCixx cէ[J-2fKv0$eMLú ;GDӧ$nt&b}G{Bָ=<%[P$qAmLj7ޫ<9ZBoړsKLELdb0ͭXn73g[W Ie}/3n縕~N]FK+ԧ ,&_ٴh ˲_(aTV T\&|6T!VDI4=9;$6E*<ܠ_@T_V<U"طg <"z˫LaIlV}$ϋ4O2Ȼ)&h4~79oa4- ( ok6,.ƾ8h"d?o8*> l){Z|m̹BMJ '$D`6{.(Tx@cGf8jWo\(Z=Zɘ,:t%-9ߓvO_6*]2K;Ir 8II@y aլ(dO?.\\¹?>5%b:Q'%G!{{¿XOtǭu}xnzQޝ) nCsl߆\M"<[ b+h&rO 46Y5W%'q ζQ;,0ȸ!@> !vc4*['l~W/E~=yʸPO~2I<-$S'r+MO؇✓G2T=xHL-@o_et~#٪cA%;X-5O7HR[AwUL@.xآ~_Phi(>|ޣ ?N*qC9oȨͿjYBR>y؀ X7d`-Њv cՙڞf^6;zϳggnK/! )%?}QQM`lg`EB&!\\^soKѬ8OСh ^׾ r2qz|iUU`gĸo/Zv48dWKH:`ƃbߠG tǮNN/V,@)ɔ::#@?v2%vD#5ѥ (xAIp-_23icJa[ii̦ ~h9цoŧqz?@w6bepktD=u]TpՏZX O"ew̷ѦwA2b0?W(Pslq:|78oÂcdqu5r;Ѱ~y^jUL ;'}$ ]zH~ Ez*xqk9HIZUݛ[+T\P-kWm}[v!E{Ju9d-Q$tR?r_SWq 5⢺}A}\qE ZI(9f`'?j2Rl܀ZpO'u >- Z9XYa.9\NE:q ݮSS켝 ,[ GA@$+Kƣ8սVdtnҎtEqqN1$3F0Q񲝂HsgE[:8Z.Eړ%aժMiJCd6ϩ`K0/9zlp(ܘB$D-F(&}k'OR3ouJ{KƯk6x=VG@j|m^Rmۭ`S <|{Di9L₴%P3 88@)ĝpeWr5 ԧ; NU)%6"G;!j^t.Ө#K 15 q;8aYJkC%#c>4ӻ1vwE%)(i&*4DK}1Ac܊4㦕W .w#aEI^!%Y#n, |0|dWJƮrC,5 $OSGk_4W]Ts@$3ʹߢ^"^gޮI2t=X֗j!W%)6+GSW!eOa)pcӱV[gbX+7_{Uu9yH%*YGJlo4}hND'x1K6FT4NwN@ %pxcN-p&\ :]b~* ñV K4ZqOnؤU'Xzt l޾֓"q3fȔWrH^LD9,/}@`"Y+"01RxncԲ̿SPhչ<+'l OT/Q cU%ib .@5r1~߫{yfTNER{82<:IU~H5(MψQuTCF<52OeĤx"6 fo'둀(`Ӆܖ sUyիs]IDH!=zEJ3p V 1 vh~={*\0*`|G%$tTC`p4n;ϥ~DӺB#Ҡ)_YO!kG.Y:tb9hxmBVfʝ:$(kܟkHG^(5ju?I䱟sNED'^|bA3! *T EsG_{% z雪 VJFl525u NiPSjDٵ;6X7ϻV8{wNv@쮤>LScWΤ>;HbrЁXo@B/B$6&~U7pxh6[$M~ hXH4`~0ZUe #&"aE"-*kQ#@$-]6{1k7$M:F"*]WF[9z½.h42d&̘xD)G}U*rr:{4{۩ z]ԯ˗7$[G<' Hb}%AQq y$ /lⱰu'C}`R+zR0҅6}FךCLP֮b`NskYH1u}v v8MxFc-{SoXJes.}j`l`TK&p?cP7E00g1 CϢ}H" `m}:ER(S禤U FFn[Qڲq2iZ3M9–tB Fq*8Jc#`ko4{uZn-9ќVMP ǻBAUHuފ#x"a~*2$"[M`mRlK|%zέ}?ز>u cy]Op!~24 ]T9g, ܃ Q &.}~n*W@Gx܃G5Y [XFBQ) ~jʛ=i@G!I^d?nQ,ݐ,^(/ \ǜ|U=a?0bw׿!k߁?sC+VSM|P[]ҌIqY4 U-a5r Zi 1QXD[-{+#1V{gלPbHʓYc˹T$9XX);XTֶ_ZZUM2R^pMޢWD7| yHq5vls'L-$ʳLXu.Kmrj*!&+ixj\vq9^*%TO)כ8&_SAZ9;4q70r+L9q&piw9>Otᄵ̀MݓҠv+gv770\L;{NsIKFņq%q  4ZW$Ӯ,(3Ѻ9C؏(J3zUCvuDgPR f|Yylڄ:TT2zr|Z =Kym~xdρnl %M|BoXDlLXJG_ðQ@⃺q: k4lבz_ H(¼4j*d<"%eEMey0R7`L} "W;F9M3}^'SJ/2`IJ~S8&/R=7u=vi:>d"-[ }DH!.QMn'/۷રۡZ8Tw94 SO}-;́m$F]` F4#LKlZZ2~${C4$e?Be-2S8ufrKDw`6zM|Jb>{UO.~RN(")/dWD͂@9.OOAlw|̐!ڻbj"qԹCjK0/Tx-ޠ_[ S_ 9JGOeFo~nq&VO,[ E6Y 4B/X##֯VA0wMqK<ӹ*kv;*u$^/,%0~J3u%R$z#^T}.?35y"h(@8eJ{pסmQV.ohUޔ23a%g̅ ל] pk=! Јw RmmG~q&u$,Uph$?iyzE1T_ l04Fokqf!З*A߿رx*!W2^xm_b/)Fr.O0"L'hݔd ?,հr-4ÿ0FkIR$j77bB_yȑ_ׁeœy8jKG#LH6H!N+\xIzKŜEX"q$ t샄bD+9Gmn}[?of슣1w(cRyGAn=u5)^pΰG\MJ  jS( osHMd gg '"MaՖh~9}s]]2y3m;C'PAQ0  @L|#LZAr9QT ϏȭԾQvQ1UW~ l7'Y3Cγƞ{_iNCsykƍ9L@/-Bs҂ʎu@̅%a0VC L>q??㴷Q#uf%Kt u??)m;EblC5󶚘"]p&P`qLۺbR'e0uS "HĨpjv32sWB ^P sW!%ÇWAqB 7DG ZLaSU-<4=c>)f2IIvPğ>F p`B5Q.sbyR{hV-u\I9-1xQC$*w^d@"BX8Mhݱ zE~&@1PLP.a*,JB$Ճ+޶{{Wt߱V=+["$ng3A IBtQ[ӗlr-ٯNSdͅ)[R| WʘtBpT ,-4NdNܡ9 ~קwOG1/ӑ0݉=ysVTo_A\LOx,OZ0R{f,/|.+{]y>rv$ݤFwVe&! s(༅!gdy.+LV~uw^9E$IO+E |'X{>ͼ0ICg")~>VWUg]l|xur's Q3S d E> f~is'wE+1Nkscl:=O˪2bF)ýh{))j.qY. CeCw}{U gRyMG4BT.6˒x5VabHgx_s z`r(Fk[{4YI0S^F#S护#d= X8)8yLŤ9ڠ>6Q=9/ ]ƪZ%H'+ʟyd5۠wF3C:IiE {i4cWoΫ_}SMtc,f|ހܯx2B-nRca"k:sOQl{D%)h U0 ھL{lۄ3gvV,G$l=C&'ବs,|cG &8R/ķ9~QBSY!ڔtlE $!%RM)kifzlס8MwT~׹u*1/QTL};wq( 87 Zdgrޓ8aޠ! Bg &JWJ~NƯ߲w8, _ `&_ݽQ>|L!2 iP@)V+jdz~ieFE+UnB5?%uP'**ncKHau8̐KTz kQ}:/k%XUN| fZ=쫵%GJ @TwfeXL 8ٞg"ur3 F- Z+Aw R~[ Gz[I093,)>[NF h~=rFct2`'g|sÜ/Ps~&*#n˘gOe6gP-z6Tb]e]*kִ=Fk!u' Ѧ6 P~NJ@ DžB>9WZl cZ\Wp*J,527I&kx`"R:JuRy5:+QL!`adKi.$:H4XUЛaSzNscCU9)+s΋&(ljbc& ,0'__D&8dy<Ȟ儲I #&KRݵ*a񅰨% B~o  9h_{J"'']=RǏ[de :p%loN-&{B>A[mG(41 ٰwcOԬ9Ws(E$ *q@}~ CauI^Q#Sr^5:D:,.ЪH+"Ro4_u[cGa1 Sлp/_"BG3B2TۃpYkC'yOmuy`LNB['EсpJaP m̸QMbeSà8Y%xӖ]afPzךX O~|L%4 L%AlfxG|bVs৙W&L-R0{{g(W9rYId5Z N_j*3P%Xc}RsF)P\G>| J ~VabF4lCHV_LWLNQ2r Y^)5-rN*$z.}~et+7DڔǾ cl3 7v$"__aԨHS!fG~iAd(9L*8],oIv*S)8"+iV]j14:k0#~M!VFtgydq:Jh!9,j#C4gO{)eE %u|GC~2%l˂`g; ufl>xkXB݅\K21ҴazQ]MO8}|YsEKřa~2 c+Z"x׶[W)k_]hה&x0伌<8H^ %; V.jbCwxAHO9hm.5 xC<^MqMs1m"-h1j+p@zcc&l8܇DY4&)YFʁ6k>}W֦JȌ1͜ *i=gש:U //Z-5+f m -wbim09qF>~3I )(2_|Ԧ_ }q؏%=K|,jY{i(,T6İP)vl baf;XMTQ(~N6K1=*@ }vhڨkgc!&C?[Pql!kڴkV/?8qt8zF2\3sU11q5-z7~QM2`Ҍ`b˚,zv! Z BB.Ri|a4#W/Z! 6+7WZev7"9f1Х%r"9NT-qwȐQYaK!?UHӔ;~.N%56JG^۴W&NJ>7b5C+BtQM5vLwIlim咘׌pC,{_EA;,طnFǕ :QK!R2!(QEy懈2ܺ6j9l:L7^# dВN:NaoX ¾ݼȔ]~nOiXlDv-rʽfUr_J AXe_Ze%Q ?fId|7=j>%yk] Vd:@XpN͞ =~@XgˉT7i@ɒ> >5{~ 3KG#@\%ʼn#ƕa9<# ͣ2ql< 8e/gu;&1cndM`MXfD1zjV Yy* N`*"Z h%ȽYi`(By { VӾZE R5  `^Y)q LMqFz8͛rsO.I!(,V h4iF*iD垟;LAڣ ?65oA;J܀䰀VhX]9c$A7\o/eڔ(D75szx* :E'?-AW7\'9S||Xя@jB7ơ{ڥclUW>⢌gD=H&dk}*<*z:5;>ey ʭNձJ[(; yBBF!͈dz6hP/=z><`:LC\'-RiaHPnoH,AQƧImM3G9<[_ozܡ"-F',^;1bPT}Դo4횻c} ЏO'suv+793t Y_- Շ5 KJr 28!:qyOLtAr8L$^ yYGӽ/C$=p_":_M«e.nS8vHg. > ~VVZ:XyV|PӦmKSjEOFCrn $u7 KѤaTۘou1/ QXCs -Π>V#J]" }UME khQXc(V7Yg&ʩ)Ez: AjG*bӣ"ק_"s66C>M~A|QUi' *R:ljK,6Hs\h/>Ta`h2ٶk3jzo:ihUF˯6T*t W>P$l$g;=!k(u$e{3Cp&.6 b"R 6G?>sX7o1ጊJ?:w|2 fnkIYF WeHE<''` RRb.;w>:E^˭_S \7Yv{\b?A1(F -Kg\@UT RhO,8ϭ5z呙i]ЩʹDKPބj7+l *&R"X+V,*#C>_\3?P"JE8fN)3#C4[o4Ďh1+~BQ8Ǘ3UzT ;AZC9o.lKP +sqm#] Rl[ b\"71TEb?pvG9tpXЋ)ϖ%7'H qSyF1:: JgDز#$Z]Il"-=ံY3a͌Pg`uPC28%*l.8GS'dRf[IC2lO5λ>7mv.,=~J^,A9)seڡ*BZh-Z)Mደa65MIk!AJKF~LQ(Ճf(F?mJ(}hC FݢϙVX1;΢p5AL\D L:V/+\_l Yܯ5 Q?'P6*E!O h6GKgBNδ sÁ .XzO7Ռ wq7-jǣR_B[Axseg̅HN?aIw;Πr?(ӎt,&O[&w4 uHhsy..swrg#Gy5NmJbD$o{dk`TiD)6wkp:3=ujM]Xy7cxgz# /#Y>$qW̽l{Nj /g5 tRMMu.֥>_3nk=R4|YGsv>^D'S+Vkqh^?GVN"x@dO lVB,ŝЋ^kط,TZW۽ 8Y5ksmZ ֛sK7GXuK%}\8ecnJw,&vЬO"\ S 0S*4fs@!NLC#ddJh ;UK}M5 lOwXMj%2haխpS:+nfޣɿٗ ;b$ׄ:GXRZ{+S{MfIM#CBI2/8J0;dz.1;M66 ?hdYEgF7n#*Y_[%޸!K!6c 4H7rc9Q1yfZUW]S X y⥓.v]-up.;tzihz˔H7Rٶ{ ˑ\0 .;f|R]x)ZoPh)Aj1[dC)=Sӟ-STM—u|z/OGV$*a}̪QLe *W6@+ H|ߝ/SJK ^6SÊ8R)- ƢVYbw/dEA% ̺1Mbnx`hK0NlJ5;'MJY9/e|Z{L\(2}f|'E4 Aa"-a!5N q &On * *_vɁc%ß!g+Ǐ՟MPFI9<%ڸb//2⪊gm`Ph*OYsRzY\ÕѩoAS ??ݜE2k~ꑶ[^L'4yC[WAm6loU}|Jdl솎BFM%zn_d8bSoOan-؏ԑ8,k螪HA].F97Y6$M?[ֶ؞쐩m(*==R5ktT`B6I3 6fnWćE-6{w9Khф1":a 4 u֖enrr$isE]iMxه^ZlW9 ݡStJr) WksX18cR *af$U-3.* %`AZ]|+AhrP 0e]Cj[7W>#x:K?]u",>2}f!JP9ї qd<5 M&z U^717鵂y3@ʶ#ګk8$_l#w9h(u}MsLr䥗!=8\̛T<ҧMSĂ)mQ.^g[Nޛ֒los͌}IJ:zWM o Z&@D."sfԖ1xfz"dW &>ORk.9+4XwCMQE2ݨNyBƖl[Ӹ>n (4lqK(% V<+H1bζ "Qvy&0u$ M-bW*Iω2.kvekttE@>g6>=>V=cWh?_8qY4|.^|lD/NubOe!{v9jB;6خ S8(X6 TЭ@=TcdcN ?lD}tnC..V#v;T229_nq .T{44b(l=G0lX-Ҏ M/0 p4=Y;0cX :)G&&e*kܱCE3J//=3J0E>i!D(ҢB'Mȳ~07V;hfh8(>NKsPȨIU4 C`fk40iZRְ@LZumm"dYc/@LYM`z7 ۍwr;eKR n X#& y6'/i5M:tD$.H}"pg6JII]`pSU /S A*[&D iQX0i9^ilfpLY/` \4:-^攺 z6<3y &{;"[Ri 燡DPxףl6e rN{]5 p3 JT~]%K%$s&gW!Ah_}[jt|K!tX;EQ h|j-]h(}9ߚ>=C*wʚ]'GDCߛ/C@M,t_Nf//J@O+-pZxJM}s VWQYx{C\En- 粍)pLDP pBˎ^KL\4F~yZg>CwqXD+Ax}U(eF&͡k|TzT\s8n۲5cSI,k"szԉ_''rWs`e1n-dg G%TtF}ݨ0=ׇ!Ef[OZJ/{O ?ԃ8? }li#@eͥȤB8K!| ,z]:<'єjFb7=9E P%<\wJ¨"#f{h1 <1~ 3p-a1U>#ABnђ ]XQAMIRC%8IG}ln~q:PC|W!|X p= 3ѩ eWi9Z> ͣ;t78""U!'wL z~g)](]\~,7䭭>kZ+^>k.Ed}1(hQKkOfj=}N0Xߦ率nrc03.RZ^ ? ћN|\ JȼJPtxp\HEۢ8$x1q#WoMtb2!B,0G')7gҚ|EjM|k4 ­]p{;ug51LʳJ'AlpOͻ>fM8W!o̞O)s6nz09ZiTX\uF`h4mBJ#ϾŲ݆/ (~}jUjXFOPI\ 8ЙT5)+{m FфSePa>3dFǸ׌si'U}Y$l B{t{5P!N956O^ Xˏ79 +Ӭƶ:I˄۔ƙK4ѡ=&U/g*r#ҧ=A-<<" fi/.˥Z04:Tj(2HG-Tb mTp\BU^ݑI$i$휭oaj9W? cfctJDKHW. ?xH)9j pzt"Tm]s/:N4Xb*i/8~B;]'h%V$Je-Lmo.1^/ V6,~Ue"_U/9v.U4P(0kpyTB'!Z&htM#8/5/ MJĪͅ:毸mze16C4N+ 1qV_-pWwjTMdtn&"7m2 n-X,ZTׂ aaJ7. $wV8vzz B l4cNgy-WJ(ң`̋I1sfΙDkA~\3r4l!yXמ6R[1ySo,gsnB>Ţ^].p5|9*$&YzTc檪m c2.2-J*R}IHenoG Йhj֩<2@$#^Ӓ)m [6O 9i +`$ZE~M$Q[f:8+$`R:2p鄋7=#H{PmSN;G$v"kP,D7}\Fuy@7{8 b :hDX:#fW'UyC< =I>V$H|0kv3eEkN9/= !ΎkH⽬v6`%co 9G?6<6(lw *d^rlI&0Vܰ'n娫0RN.K%~Y%  Ͳz>[=$dWW&-Vt-$#?lB\yi e;븞:͈- ߄^z(%%Ÿ"Ҳ4.Q*ߓZ:Rof"u;-( 2[Il.A>mjMBw Oy{$p`dtnmzWq?P e!`ʬ$ŦаWP4dcz` _2C+k)HwAc֌"tOf8ufOpgʿ>.n: {! z!÷fz]l;G ] iΦqY/u^ne$W͖DX&a vjX b3z /Рo2S{h'VP؎deY@T}4GR\xm;7@1ۦ O&CQb(2ڀ5heĞ܀)4H7;am+Ec]ёTD8t8ǻAҎBȄER{}voA* WtoGZSx݂@aE˜7Q2O0XvTbvT68Tc9޵y6О_.`1TA7yW4l8{t>tGY>[O^LKZ ¶m:Mun]ڬHi<<yQU9#za;} <:!@]P1"d8{ ŵNjnvWX*%a)~D^lOcإ@yZwů"5/ڽAu,Y/,JT/bHBJ>w)ISHv 9XnRPݧw,;8X e3]S2ZƧ&M64<(D6:Nt# zc`n=>}ISl(8+y L ӡv^ /[#Li2i euBR` KH'~pa.$v.>3ܧ3=Mgn.Ӄ M)"7Z!O0Hb?}/3NI[\ϰ&+:/n,ZNdЛͺX&˧~l8j ;l]Ucm%S ~12jp%wyx:`.2# A/^ +W|^U>):V#naHX[[޴@Fl<>),BnТ/6}CٝWVEJ>;-D\ WC K,o+v endstream endobj 104 0 obj << /Length1 721 /Length2 9844 /Length3 0 /Length 10419 /Filter /FlateDecode >> stream xmyeTL-ni www @K܃w N%Cpzg>qthy98X8X2\\vVNIvtB@]@΁Bttr[YC :v@ =h6klH˫eU  ff6(A  /Npp@tq(Kk˨h%ٴ$@ q_4_1 `6@V`&`w?\Ր`vSڃNn @p:yq `W92`OWjK_. Av^s5MR_UU[-`hvhBj to៶o\q{ YeDFpapr88y\\~]s7#@ syGsPr© x Smy ClpqdU0?:Ki4OPL]a_>N\"85FT[#ִk=aqA' T5eVpn&vACdb]PaP Ծt"N5#!KvKD[ZyY S!w i/c'˼-Κx dwS7 GxZOƦ7@UrvA•1Fw8/WItzծ~vF[i\BsӂXyy]3oseȵE<=(|_L:E-E; #-rٿp1%BBEN%SD0eʘ'ɰ`Kkk 87q ll#_y[7F]Y+ᾤa ζ_NjgL mfe ]?ݨҊN`uS.i{gx6q)`a{EMFe_Rb4ƯIN$61|D3O8+$ȆP2Q okwWMP#As!wgW"YQ2PD;Uϛ<&sUՐGg^Kڋ[B}8jכ2%/`_v?:?y˩Wጀ;D4.ضЦo'mC0HW`'_$J6(_mw[ф ƙaLPBWf8RI3\_<>ӽ^]8 p΃KN$@Yccc4`FK q{9,ll&)?9Ǐadڶ2nլ-M11/Zee9z̘jp,4?>ee?7z" ér!s}eE(^(EVuWM=н}ܨWU{)$eV˒MٛY68I=ƩhG(+%<:5(KD[F}όAN9;t) -J4MU[;<Jmߚx"wTB"% rcs|ތgq!ɦH>g2gR]H]{C1 wIU>Nać?xpDP_m+0Lp,%[ϝu*@ԷdDP')(+҅O<$y`8OCBPJ&sK@^"`ɜ.HJ0W!,.&'jpݍBJa|WvVƹs "Odȧ<;F5+t+/KyE+w$HO|Ԍsюj.{t,>65B,jxNUO%0ʹ۪JjR;:"Ù^T0N,ctfx+T) W?6Is=[<{Mi5u@sɒATEؚ3#KiSZS<)\>+\SC|= /-Q23^f;Ys3j4gRjvۦ.i%,6_9,fR]} y^C#Ⱦ+T<$)_P smn|5s`9ϐ;riJ^|jEcWr. ȱG30R]`ZHm[sۺ;ǽsu0WYik'赓" ADD@^_"(6)ziv e1a8Y%*]~գh )Y? 6dc=Cn\kK|CEhKhBKFHZl[#0un`tvTs T<&YN?GTģRf1V:6Β. o/5IDIgn0~t4?1=~ՙ:.?ד"/#a8]8!ڛEc~dyؕ>k$K!s5bg ѰR 퉸 Z&u#< f#ד0.+lne o1 usPZȥ(U[1~ / 2͝eҊ E +?™Q>C,Ǟ$ =k.!,WHbPj QK?%7nta[>ʊ=~,eRGm&ECbMeoT$BsS%%]%Huc*6MN8phDX^&v?.zDE Ex^u*oEUOfuM3'^9-,Qj|BRe =Q G M;~$aBj"YX)XXBPznÖ5|:!}1_ ⴊxTMd g\Ǽ6xE/s~s#sVhtz3`-e!\0~|h XUT%x9etT;\񎫰j\.hFž{<{AHrҔG2 XgfgYl׺?,~}G3]*cgV-hwvY Bi댠^-<2K"?SԒ/VW /lE,O`7ۻL*KR@I T#0v&18kf5~dɵ^MmỈoڔkL>%JsRH3N,`jV"#\uL ]?dQc?I`:[CKP+eF >251f^M)/0u oG.]6+->+ewxJq-?Vmn}.JBk&a*.l lu:Mj)sP].-1lM-{*h{>+[JZ!a vr#и1ƻI4~C%duL:`%չ4&v}#e̓Ԥf^ߠbHX=aQp!^ܠ=i3S7ݧx5x+yoχwA"WߘmP6ϤҷN5ͦCNnNHF6 RϠ3Hw{ZCUAaؖo 5V?\|1N_@ZPwhL ~ 8cا~6R.'c^@R$Z |( CԊ4;Et5{}BDqJtO#L\v$0<"0UjEˬ/3r; <>Qͧ~6PDOS lSGnfG$v M_R]F7N㼚[.OêXL* F+?/҇CEtEEi1Νm^@8 ۃRX?3[R;!DcBkALt²AhK߶#9J6̂78皴l T6ݢ 3t3 4FB2.uIuN)n|}h濎VERO׉. ũsG`هVT[$$ā56$ޥ41at9'd&j~r}SaV[j9uTϸ>@l%l$*:BTZ;Qc tIWs6*dQy[v WupD}ޱ6|+)z"\_ ]@#;C!=ɟم@c5+Y\X4Tȧ"]S# ؔSc,4 WU_ A āZj) |nA0D6$ۨ+x}7&p}b ޾p-\[q`,Bz_#z[,`GwM{1 d4 ɟC Kw"%} 1CaF=6j-`-XP eƏ3&PlBa\ A|c\ݽ÷en۳ټdW9̦iTߨ>|KOml<^vm%bnBÕe~CAdֽ.Bz DuAI2Y܌ZĻU70Qt^ |B>ⷦ㮞eyLheFc| ;E.lb7w3KL>L;$Gruu=+ x)S|<^Y֬=Q>aT¼'uYQ%'a ٟN<VBwt셂UG`"SyCdʓĤl`UK{C!=h.T,8\mLLaٟ(PuqdMPB/dѿ7hǷYѢ~YeB]hWl <u5XG(g)xP֜>fy> O?;1C0ZŽqHAǹ `. `lW8G^nRP j)Pѯ.|%iYȫY'g]绂 y$ܺy_waSSW\e񰽫a"9^ஞ V)Y'#&m+e VU<so@V-ֳtIYe*SCq=s$ff5+bx ;|G.j}~c^ n2;ǷkztW6<p>:䶀Tʧ26&faו_#2;tGfiL;?89Y=YxyvdO~6B^x}b ' 9, oo{JrƂC"[S;Q9k?ҧAϹ]<\z)qidT?--FAbv-(T*LxiVиdh-<'sjn!X#}w&R%S&/둩WCi&<ÎtTWK?PAv~n)o~a[NrB;r`3:ZLRdaVUfE8Wq(Qy x>Q)#ǹp/Vq E(V;PEwYuTY@E!U;&WvccjY ݛAq„kٟl}an8x;@PZ}$ C ' tU&H3lyѳKwMP`D.S_((IJ,)ȾüxaA댪Rs)P J~v[ba|C|;]FU$Gέ5SGȸAx7D8~V4_[:xSkG Xu ufj{MVUq .M%ߒlu忰@027m…99;^jQ KҭEFހufHaSR o6{D  04Z8pR"W">Z66,"UA.u?ɇtvh:F0aJuInшSyIT˵kY"Y2&8b̍m nC;+7eSVeq%*Cc[͞߻mg3 TEZ١tbZcŗALO|o>dKpL8bo=ײpVi٧;&F]v["XE$ dt6b qwR2\Y,iL0nahvњW*[jce1M6.i2}kJVN,w˷֌# '͝CFȄAX,n>=ݬ},}N ny݈wED]~[nP16TX$Z67ꄞv4Wr6E:J!{^#!ĝ HPфCmb@Ʉ7ݯ(eeYrK{epaSGyL"z}Y_BW SZ(U76ؑv%u~uvdq5m,kB D)'Ď!>l>~s?2V@c&ķ1_$ڸfݥ::\Ϗ1[D* n89,ø+mtXDy cl;үPP\( ?Hןbz]^O$)b1t4W'KޟRL4\CoJ> ƎtޔDLcQb1iO(ٟ޴f?K-5蟝ƧxÞΣFY FQב92ǽQY;K6/yWY)OF6~X:lۻ#*c@D"qgXUr6Ir/dc BJFf< [w`x9SkibuRJoZEWQPu.&ʒRCРUԄ2N`o@HboryҜ$"q 1^M'*1u`j6޺dՐTDP43ITOa],{H`-V$͟}n*ܨ`[5+_mQ\;.SopKw)6a$E<ãhb}(qU&`xzvA˃xodc%-(5;9p& 1ޥ;|d")|8V Iǒ?#_?*w+uB8UO] b쇤SҜ6 TOòYx9GL`$1 y',a>JBiŢ߲xY]Yʵaƫgǥzscw<3rza|O>vs[qԏuEQ KN\)4A0uQ#@cck8OsٝQvAE԰cs*XʒtO6 8:J.'Ԣ^ۈ5p+t jBQBݙ6)=B={ 8,%Ryr.BU}(W:wK,Xv|Z5IdU_a&, \ŷ 7]O.B23[H⇤G\ [D*JR[ImyAHEk1pSSS4@w>ˇM! p^߶L+\yX nt yuYQ{\%Y:)4QyuXܫjp; MJ˫z _,t]Uq BaSDz;G!24&I 辠zDy=˒jϚU&K,;(K82ln<q[ ٘;Xzen8 qnb7k Oy (/R$y䜣h.({IҔI+̼0F,~Ok=n07Y OntY} #R蟨H\~}E>_1/Ŷ5<(NYtZx4JfRVAKQ:rNu_Զzžb&9ddD\" _+V'zHl(m_ qv[j-šjѠאsbex%]WH0 endstream endobj 106 0 obj << /Length1 721 /Length2 13499 /Length3 0 /Length 14072 /Filter /FlateDecode >> stream xmcpnͺ5vضm۶m;Yb۶X+{}:U_?FWkYMF$`hDD PWcf`0ҳ8Z9؋q4L*f&f3## @@iB"@`neb uwwtwqwvg `nekQPԒPJȫ$͜lnƶV&Y+3{3*3? _\Mbv91U!qyU +r{Wf&vFo?= `lfae/ݤ)9!(ѐ `jfV @)`j s05s(;edge?:[JV.VfVHmndDk\L=9QS{[1!6m1{S+{ ?Z9omE#d&\<:㿾Fz%,Cca003ؘ]7gg3{ ,m$33O3?&<bE4j4Wgzot-M pUq=炌.w[c"༖TZn+5D_cۙȃDݹfn+ROfJ;!K0w#$-=KPQdiÙA +6x_ك!6,r<. qM] ^uŽp|.Ͳݘ&oJ^)d'ߟ\𩦻0Lm _86T^ iESNԇ@\NéI~942lb1ݚa1)_C7 2Pc"fH@)dWa}ky]q ?,:EaJ ;+Ep},Fգp"T4 4Y҇ j&iLzC'Gفy[> ц?.=|DȁT*mOrĉ^Rm "xTFsU\tҲndyl6Xı݃LaYbkޤlޑUbl8e@1ND|s`NjSU-2 `$ |kR7ҳ_Pムy5d" gWw4:me`9PS>&3Gb%:M4y$AC8!̰s:}^ lcG]BWYWm~Fr@`2!8z֫Ka.C5䕃t4㈛֏B&mDՇ[>(-U}% I.kh(H#niQѪ X7\4Ga~ ֿYqGuQU Pup"bφ4l7z_Rfu@ruF>r+40-ň+ImjeYr̦0OUG]Y h-KS{;?O1?pĤ<+(̇xQ]p }ڒ GгeD?)Wn~5د]vVrV)g;Ɓ&,%(̣b+ YeOTZC҆D^$61d&(iq QM$+ R QϼHx;8iǗaٝOr84 ^ss̞U#NP92ũ]@kE3_4,_(R9NEfQN끂pkG8 O# ^[}F"T!Y͆TpTC-JPīP.e,u[D!Pi[ Om=I_| jA~w!;4=Tc!dKjogT򰁁Z X6i-]^eV˅fYXHXAew ß#1):ہO(%AxU/gYVJ:oVCt8o~dboq3Aa~kd\ם@\;ܯԎ(eYnж d>Qf[Bl2NaWVF 8Llj㘅mf/+ԋSFS† rB[B'sns.C5f{ DXvɧJ?1}|m'[7AyD([} ㅰ֣xrlcnթ>U.XTZ>R@vlA9dM:.޻nPU}˨NI ;d88a i5s l16 Bu[b$*k*WLՇ!Gm% >EηA1LK毼'TlO}4 ^┍ߜ|l:i~wW -&NC26V(Vκ/xg[kv7햆'y%\X)p3=q_!{my|u+YL#2+ԚRYMp\n4a)H;$L{-|[&49BNLo>wb|KcbzeAJމ-!>2Vn{PR kYCwh>=Fi4 ;L%G /WLǾ4md;CL/l;?%,S#s\'$?/gޏ0vt +#۱W"'܃ܶh' 'eJιJjxZs%a+:Zо :@uM=5,`p~m灩I/p7_xQwY.DϖZ-w$+82ac~ɰ_zAd8k<ˈ@CS~!Cc^'w  mk/1]@쫟z!)E/`s:mӝ>7? WCD^F4Sv(9l 1M&i2[5Yœ18HUXs 6o᏷!L6u-D>[a ogM2 g԰.,d{S 4:Pq t(G¸&0_ W_t]W1oegU1Pf+fjrh8Cͨc!zʅ{3R@/8$ئ0)޾ }2/0KjК~2Ѡ4^RUxn|[IR<\d##dzkOm&4P hN@\Tgr#|[&6>۸|LߧNw@ğ?h*ڨG_si~8 ( ٨'ҞMZtU*KS^o9 Ǯ1pQuWcg}v\kdt&jokЁ^(mj9}\VYjl] Hgj>Y#9mK.0z+܍Ɏ{5aRk~P9>ku{Z~l[ckNJْ*&r4 xZɰ/]-:GNX~CZ@9w _:ioGqH]ofH.ivqG9-ًGqÆ>1Y@]VIhCۭsEw׋ u>; RkJwQch-I$B۴ֿ 8R&,+ S j<g^CF!0`5/bj?4 RP.<|s"عЭv40sm@e}nh|ZYgH1u} RzH#"4)Sg{q i|F0DA\pC3eIN R3OTs3w ޽ӡ}[$Tc%.>NvZ 5Ox%.)O[{V٪oƇ1rCNϻR }P3p#@jl ܳooٽMyX6iL4CWb'(T9kYYxv\+a(Q2Oy$UL i.u6p@n+꼀#5T .C ӣ:Bu^RCzc% R9kGmB+_"\ܛxdGG|E̾1?5\~k&ۙyu8zI"bWiq &=Ǽ( ;Vm~n}Ah!<|-$SbiZ#U NF~@H&'%KffbjH︧*Aێ `BpTSg}vݫ Y7baDddھb$]:Xڌ(@ce>mA|:-/{eI? Bh^.&}F1536ٮV}#R'HҞ5g9 UҺxYvdXM׍ij%4-t?_r)+[gXjK׵q@Z2WlJigٹE[Q?"wzMHt䉊&Ɵ` _1N.޻:\UQӹ0NR}LL0Xv=eԞy/&lfYE-jy#+;5nо p.J#J o!Ch1!MBcx07XuL,x 1* iAp.Pf#8 V{i;ٮߜR>hkF.y 6N/NŷF]_OgzFkP}Z!oMjF. Vp^p-f+>T#0!?3)|lLdD9P^O % Ŗqb>wOE@i ;鋊[~5_;iՄ^ƛ)D!#^ KXvp|` iߑIj/pp:؄anD)05d1DGPEvKoF{Y_x-NLYI=ɒh wX[E i~/W|K  W]vϨ FY2 {tkq6,3q-%T Np^ Npma[xmC`HL~_NS_.٫͟W?{.m>YS;x/4f9̠W{ j ]j)\VX׳2;\%@.Aґi~0"5?!;Pz#Yr%_m ϖ *M8#h?*oE  r_ }{Pq+\j3lEGNA<drf`\G' 㸪H\' <ۙB8PS6+沒jŹx|We!4%<$R >U(:g7 (!W=Y ^/V+F.e1rc 4spV?Wp/ ~'l[_w}<,ṮRX&*n5gѩNKA,WHn{E6q^&o}~(F+ek6k|4eCLزlYAj"6y^6n JF όu˒A6{4Wy]tp,H3}.+5"~\rx(>G]c#ND׊v mg@kh6u6bQ ˘7$L_ڙLK.^L9X%-pNi+Fz#15CˬlE"iYdvΫ+,sCCcEq-)Vdp18u>})v? )|/1f ج:943巷p ~Gj%z!{m5 B9&S[~K6,=`-0V8&wU!swɜj9O|ðE} VMEڎC 3׉q6m>72Xf&:~[Ϧu۔$:"($,Ni,C 4[Ll }Q8z>jAؔtYZ;%cЀ;TTJE+⹅!YRi0 ~#$:OƜ'#Y)dk0% Ӝ}i(AXnoƭ$gAr{^e}"5TXrosQ򳦋 *r0f^φ<As¯!nsyhŏiy\.lѣv0"yb ٱ萈VRg4V~`'F.Sqpjڝ+ J8tPevhg._JWP2]7ζƛu/gx{Zq:_wU9%EFOp?2WA&{k V*.P;{yg 1x>ᙟi{dlomubIa"a8/1ꄠ]IR0O{Opwۣ6 ݎJkר!3yX!;LFB!X1;:wfd%&_i\z uisX4fJ{ 2ڡԷL!kߙ+)or_ƒے47qn}yM hUۥU.p:z36|"c'al PiORZgE%Y&H9]H6z*n2we1/ptu!©* >0٫KrJGy#쎷晎E`%iN}F99n)bz,3SZkxǬelY=(mqdƷɜC7%!v{ZATj?@iLBN ɟUu1$HC YJwoxK.$pxa%*hYzM: ay MbÓmQp9!^hU(F,@FrS@`N32ϕ\:[ky8g : by(ScO&OH"cpE ȷp56)B,٢񨃑 :}yqKQ楲^tpU,)#q?g:'^kLl&~F  "br@09XAӡ|"&z%u]ІsY#v~Tߙ`~l< T=DݢZNKQ1[xF1~o -Ij&ŀ@'ˁ>xQ=MRZ͑JbO\$M96MDgoF~e/uCRԒR-m\T6(J|7 S+{BaCCe? Z+zb,fdk hJ@J4< 쉊eԙDIm@*e~X2AQ3O6j2Q.3j^-гY5[6w-Ϩ_Ƥ%M-"103sT[o%ɜ,FuT;rrk 1K|RH?T1jHņ] S sVy%m Ydӝ)h@}QrI ? e+_AL+Ҧ ~z@>7fuU۟lD `3&E+'ѱd2F~,YCBڊꨧ0u{o(>YeQWCh1[z fMJmUW>4<{5\m*yh2tݨf亀Mے cNްŏ*"lNRj14(j2)| fa ||@y}5^](B5&ըf|=O ʠ*wl)nѺrrotT_>Te-GY{])g„~V+(yejrH|;:׎yJwk4VAroY>S9;(z+|7Vwׇ'W5 Q#v.%gY{h1ZP : ty^q%4yk*%ifm6_?aۤw38\_Am3C#5;\v՞'p,angСKZ>:V}p*- ]aУvq}T [Cz(w@Eƶ> :$TV)7>aulMB4^_38B+.& >"c*=53ojpcH!1;1b)*]G]n8]GoY/%8mڃkSK-=h52 }9Q2NAqMR%)jw8/h.-O;5=# D@I%Bfiw_6?B=HS+{M!L+ 0)?9E%2u=߆)mQW QFJ؄1ށ+C١\Nl\I&Y۟^eCLET$5+Kq%g#;2·!]+;X{҇v؍)}wL}OBa?OR#^mkv3'?.J_37{U<;0KX7ZFi}Wh: H+xRa1Ŵ[vF\$LARD)B<S(?sqg1ae,KepBy3=ȱsZZޝܬj҉e-t` cx z'7J@ P"?eԗk|$0~$ z6:`Nj+Ty`ārK!nYWQP_=2T=?h=0]`yF᱓9!Qe7)&S6>vîOu e`-+`_exXHD*ݝ aEGBoq \bM@3\0C?F\%e%{ RY'|jFZ)96}YG@<w`eGضsQzs6 },"<:sxU2_8CX{5JUn_9h4(;N2<]eKlﴡ+-/ i)9KO;ʶ,@Y\Ξ4-d:FDSJtD7C0AdH,˹O@"o`+[i3DV)Cfhڦo;+a{_ֶL30mE Q ?-xlT{"~بR_ nMWzJJ^Cy UTkUthfmsli__=rc68a$ǾRR|bxQ-jɲ%n<5a6`3S.&:(dP~$BWR2*X{5 ifM%`mۭ-v-'o~ܹVHOULel%J8>n JT0XW$zAvb_SD"j~ 31>$BzAkAaG4LX E b6T*7}%0M;Fnz+q٪f qH]NtJZH{/}7A<*_陼A0 r,ʏ#*4VY?`%ֻ_v}TtE+9*KpAO(v|O $m jBC2Z4J66E+/O#-$Atz(d7X۔8 \Ý,(6ΚbIʃx#H߰7#-mE*fu5~ e2I~0r}BjD9b2nɑ/Hx'a`Z<.D.CAp,-n%wXÙAes$3|bI0aZSf*_TN+ꀝ2k+'~/pMv4WQ1#Sٻ6Omɣl ]V` ҁplFʥ׽dˮ vޔɊR DCt ƟJr:ˑ3ʡ0{4]֜! cכɍ_-ɱ]Yfs 22ҐQrgHl- dn= GRa詿m=\D!fo,I(sXGp P5n`c]>f̡XuWFY],xb1eLܧNX{//p3+3 怡ً:qg~|_ @OiN 0@2ADI i1CݒU~dd~ 4Х> stream xlctn6vضm۶u6Wlkضضd<{}8ccMA"Tv`a`adI˰23((\-L6N&@ ^9@ Gsrvhmhڛ8ظ4$i_aaokw/tIS{ܓ2Oy`ڹsͿkyOsGWia\ߍrCݬ7HW*iN8J3QsyZy2?"Sfli`{Qe Ɖy,pRq?Fr3%a^tyb |/F ]UT ɫ$ /jd5KZ&׫,sъA4ŖJ(D|x߬M{iV'Al4^Ɋ6;rӆ0*:J;].KLyE][)Z‹RIdI:j$]q#ޱi{H>exW{^UK%ߚ( Y~vZ/Bx BsC2[2|iMԾڦ-#=O[Q·Bb3EQL"9nIwV͏J3q)='_FYJǖkM~XPž]Y)/Fp{,S7ثkiRgk*t5=>=䤡rf$I۵׹T)Qfj{T8 (@bYžYSS 5oWƮOw6f̡vMdEsAfF#c_>K@[UVy#_φ{,^tf& wF&v۴gPȄU@rX/ƈ_+vp}4ah5Btn#,D''{ɴ)tL(x4|TpaB ?"nf lۏ"qP` (桝*(~w. m@Dh(bݫ4_^sA ,+35466~ [h,J_k{&Aa͐o!ZE臄hZוbem2u/s!9;e:cӮ[TWzcV^VƬԶ0 V-еrfucꛉ>-ĥSѬ2,لdX kiiS)GkVGvvy~R(.Q1jYH*-yPobrδ5);.! '3$F}_fUtԛWBM!a5ʑlw۪ $yMQi[GA\s80KsxhjŸ =1H%w=a~1; W|PUs.gh]x~*39g&ɩiëph0ޗ,eOo]*z>^<|J+v{ZX8 #S ¿*+l˻Z:-T؊&~w% 44qǤ&qu|S$:r@K`>sluӞʸ7|YېlqUYv6WY!(W^0qo J#H)WтxܼH p"߼V*BϠE |jo eSR!\z+7 ~[ bZ._j0f 1FQqCe|谧߼&7,Æb:y]Ĉ!QTޗg;Cָerpo|M .?=awY]) uG%*Uܩk%fedW+5`O F3!GN"U ȧNԠܨaZL砘,7wj;Mhˉd3N(MsG (}XFS0 Ut bA3oJwA][>/[S6*,p dvn.MT LczJ bIR^#)OZ&9`IMϿcv)uP~M ="y,n>Fӷݤ%@\Eg*SU< BV>`"~YԃόGEK-ɵ-zOv=0]k2X oS˹$/6=aD4~q5nfb -,Z5A-搿GTEYL>1.t6Gs=7o.IJA5iI&s_Oꤽ(ğ Kμ Ύ&M df^KݡJ? K㡫%i.,mH|r ŐtKsg#4;2)˱BFŗ6mFIAAZl4A/}7aCO-Nƽ8GJ niҥ2C&g׾%Ww5Qy,-oKV<q#*u~Oe42=[& 8"e=W4+ JǖhWLmjK{y$XYHu)_=?gM~\]mآ1a;L3k$W.xڌ.^Z}@R'#È0~-XʧZɀN|UEC \{ݩ%궥!~",ȗ%D.x#jߐȨMG u3X2FZIy?K靌OGy)zEC:.7-r2c"tֈ-4%ffOrgQYGUutv!l&҆-u|SM4՟Lr!T ,L*jVz~P*WժeKTtsHW#%*^.{]WE,YV"=/Hy_AjZ+Nh_&WhMA3Ә"ݡǡ`9.#'$.s-U2(Eρs͙Bʄ\h^;{|VjuS.CIlLX) G愨3{%/&ڒ`j`P}]jAC46P;`z^pt*csfR>"-΂ȇc.^G! r\#0!GBU:Bt=Mj _d5B"c-Q-*p{qCx|ؓ m~n}4X^ 7#w Gb q/Wr 쏕ŽXi~SL;NP&c.7تʟ!|n؝_dR:SDtiyj|ȍ&$]a QTiUΥ?dW%GmZ4d'Te-uy<\ȀA8o7yq+?ީH*OZmXumtƚ0ɹO-6Ã֓,t/r✖`zUmɋ)?ao.rfР̓ %-1TJ2X #c/`=)) ̢G{6\s=0{ ueY A#No1Mێ9/4K@F~cMXu-a DjJ˄׿p*c'QT͉cԻ\ҍ腱&xYSW}+DAHVmXM.YEd@b0d7s.NR>+R3r1?'sՑ2aL-m9Ξd"ey8[OqM٠HZ?ʷ" e2"[=&Lml;_dC{C;܎ruWKb$rq\lԙb`j*p9XgCP:kwy9ʜGH2?½:Y[' T3M7K K`>P+I@#ڥI 6zYBeBZF\[O4p&~§.M<.Ku;~uy-gUЂƝe܄VLcoRuY'b .#ɹ+ 5Qs#/cJsΫ=`v[jM_]r$OJ[~Ҕ5k9,4AXBǵm1 Kc?x-'ub;Zn(RU]&rᜬ1unZ?O.߆&as-Xmu%3z{8Ez3O_'<\dQ kA!8b33qsES' bH2(s.\pȊ-aْ"9U vIt9K<}4ic{Ku<=x )EVڴhM˞oj8]j 'M$ }>w0c/l)f̷l(#9;ښHc~ |dZT-mN5;5>{I2 (5}Y:{g"}ZUT`$—)_G8@(~k/5Tj.!˧bl^RU$'sz_}BTr.vf'H ܾpq{rK V+<$ `Fq2QVWOJsS0 :[\.|ƝpUa sVI,/4 ~wtREdDS_Y :U#H~ysZ|@v[a?詥5 I7c]|8GcG+^^FJDrsqzCz> M/XS6+%ltKԌN{]z0le&cDիƚA@@)M8yBvZ#DVѢJ}/;@MUR]+DkE1! nDTb3Dy>?`ߔXIYg~9}i*p1aWƗYNwE-Fg6TbnlAhMQ&5Sו@VY{^k{I~󫁨<2Il\æ!*t>J/c*,B4ju[^Q6K<7Mf2 :óۥpj}qoSK6eyG'667WY{L#Mh2:z@|bnQXB~u B~=0KU&z &WcW6qֆ$(uWTo fr E4+J#KP+$Gu^yTo6ɚ3r\\Y$%0.6Uf:~` ܻ:J;m"*ۇ) ,q HCRozx*eМ2aC4^s"`CJ?cq!n`FB-}`.{M!KYjhnuiÔAxaKD 3XSFD1}\y& BX>7>l FJr Z Ù-7k:rEzD6w1+{pJ!+|ayG;m)+IfN ަ sݶ(o$(A 6q_׼awfyeQik>,G!Yx^|MWh20AA/-tIE#ILQQ2B!Wg^GKyx$Uw0Ex&w&9_?[DQy~rόՒbg?'$%.sñv48dԉ&3}:!w'Xӟbod2|`R׈H$DrNT!ޙ.#z.JM^#-?]#"!dJPBaex@]ɸþ0G-vBMf8BoXH.S[^" 7T#ښQS*;Je(ʝ2K41;e;fkz2I!RvadDЮGYF%@N1u֏#_J%EOcXWwGgNF:*Zi6dkWr$َqyКI@Z"r◈ϻqR57VbXrd2"hS?w)Ɉus,(~pxRhbLĦpEāWGT?HEfF@C4scA.A2}GzRyȽ_/=D\<38RVZ:#k~dĖ51F~7f:Dr+i/(3t)C~ b %g@o6)Gԧ j+?R>vhj1/`U6:-iu_Aӏ=9>ٶkE0 p-x ʤjDf+߰u/[.$6y7< Dӳ,urjyL2=! 0{b1s-fc^9J罱ZΌBQ94GÄIK?nAHuZik]?ʹm1lcW|% mhoûZvoO{%MvAYcoO8 @̴&z8b&iۺXhFz{d;ʕtmuw܃Hgk]rÿ 8EP}ϡ gG2E5Ӿ]6ЋdI[i[ܗ5xU)-K%+Ow 0\CUě$ws^"70ü@ e?g)qQ}p' Ij[(t $; 1w2QaBn@ȕ1(4'VӇ9I!DL' 2$/pX!xc#d/f'IҁkΔva&YJfWZD`-}7I Ank[;:^*"7݅']W{Rݐ_|˸ p`'j|t5/0%)ybԒN-ðxN$7h`>ɟr(hGNGy N6s k@LY0J7C6FRv!n[?pɌ˰kTnJY܀r:ZRvm8IgDctYyz'uD_Z|.L;`nmzE8:ki?Ԙ۾-U-vZY1+S1|08p&&R$k,`D<ە靮@ۯs1u Q4;!y$Yd h@N*cA }T͍O[Ĺȋh*;ܴ+$[FSt_c;VJCVM /_fx G}xŞ, bŭdM<,PJǤ++W1sM9?-@aI+w%.W,iw).wP?P#tNV/;(RZD"7R%DЧǔ aj%nkcR'[mMȎra-O& ~raɷHoNGyX}:kP2{")jsS&W<\뽕!nS A[zr4mt( EN UY16>B}3]6@ ]@Y;OJOK bFT>n6bw aqw KaMkZDXҗwS,ӧ9hNZ|^v@Q!{@mIrPGpjrTYi(c獬I1tAc4~ ,)uty2^PKMG ,\sϭ UzGsWz{Y=b9QrZuh@@w,EWۭN9nX- 7Q`0B.Gw̨~ؤZƜ"8TEDA<-y]L{s*n̲St->Qv!i,YnH͌z|=UUZi+&rj,q+7~Τ }XYU\,(.e6zl>O@uٵ!}zC([( ekǀ#"KW~ $ m)>IQK3:ƀoCvʞڲ;!tz} qn8Qo):tp/GCtٔOY_4֑Gjr>1X[^&N&h? g'Xnbg@$B- ]A3Bٹ0pDtS#5DJ.Ra MZSC1e~h(`}'ųGp#EI5} @]+2|APE_GnF۽%R d'5[! A a9+ L;P-:.|9 oSq82ts=@|Ws*n/ż]Hؓ!%4$ɉH^laӱEʹm[#i;u+kjW%-Dw"9W,ZU: `ʧn+bktt&4QEoSHߡY/!9t5$Gay;UTT<ͩeqoRQi A~|7XжZ_Ϸ.U=6dI KaԸHԟr]Ngӝ2!zF9,!ST]jYXvn"Μ3ʂUٰ8L?<&N} qCΰReZ*XٚJefB0 L{ŴFg"%:~Ӻqg<2Lbl~:gkvz q2E_c>}m6\@' .=g?cX>8#S; hq n+\q֖14F=_Qb5]j/%Ú3yCtנ4X aMD-unߖS]qnS^K܎+{D4݂ VξRao[1:`;SЋ !9:<%BT…5KJd0חL\j2C?ʑ]>L`X9]~T 99P&E󛏹~q$_C5rQz6a#] i~!aXgf6ئ x]dUҥkLZN|Enq}.dv63+0fIydJi7J;ѨUWDTu"EjS4Ÿ p##5dJ}|dW}}.%?r//\~ vCf= 8yA~4y- +Ui5_N)F}}۞c59m"zY]v2im ?&ͅeER.JaGu͵݊@0y'08̍I{gMD%pPRr0OmOŀF{M@)LMAR+P'y:ܗu(\*9ͶKU#ZP`wMﲟx&5N7: 7md-abVy9󿮑MZ>3 7'PhQLP^_߻Ԧ1ʜz} Cgp{̀ձ܉`̮2P/'$}3j)Hס HW}{yXƱn>,<0*X v 7Yshz YJF@k/OjL*\;A@RTyX&݀۟\swJ֐V.!T,@E -((V*\W%3#= \,tdƞȁL#IL@pbrWllPE:{\0t;eyQaqC7ZߍڛuNG6$&ţ$Pk$ U -DRNxA̙XGT-PKѩ^9Nkq݀$U9T O咬Neє2dOԹs@EW!MKEQJP%)KAF->ņ>m 7J Z<~۵ Fk8)(%'i*Et(@at] uM(lMC\5uAABRYݦ dg|x9 q6vKܞM[KY LT*i?=i] Hsb-V!@KUNcDڐ{GbH}rrR Sl:' DUK0h}gQ}˼&xmD]'W8s[tޱhZXCq*+0>l5& 󾘟UIMcfV*ڴ[OG v;Vҽ'?>4cdKWvG.sk뭧vd#޻d1b5+ϾLq@MȢ xfPFh-j.E.:xqN#W ?mRJ3Q{HY.#[5Į5%U4.ocρH(‹ rsijbcM:ֳ-U(-'Š'`ct eB'ȻF}p%ٽFy*O ΡLo^ǔ+cicȳt)5a3]H7زAѦn,*dkvT=>)鬙2n9"r ȏl)̞KZ8|}͊Q3btU]E4By!7grEJ\GaT9J^׵XO Ee$ŪtA6T|!!ɘQIMN{{K7YaJ*i@3{e&fƤ, 'XVgLb@!9qr#kʂ87cSq [ ),Bsg K N /h'Rj)q w(Dhu7* `l >R @ seog?b`!.ЁlCVǵݚJbEp/\k'zy oȋ#αK.٨쁚O i-/DXfOk3Vx}GZ$]~2϶ zm]fsc>ӟ<#[p쓤*4qK-:X.NR-Nņuݯv*4bR~{po&>S_exBf\Q1Np>z HllE9(_-zIр2Ak9v/W5`ךr+2[lMLը=ymr.b Nԛ w]&oi4[]-.مdti[݁ǘCu2ѳ2p|TʹFkغ݆97am_+`++\ ]a+-9s`XU?:iϹ!Dj0"--)u<^q&S$ǚ>&bȾ<ԝ2F^0XVfq}ieIZ$Ƿ_਒(rENZGoRS)Ĩ,ϯ]'4^li`N2,(Jő^VPlkqs<)Pĥz| nUJ>zƾbMD*J[:fJMrB_5b-ӕ Йo КE~"'Gntt|\_FԕOkZ`qEAn)|ʕnGBml:gsO:o[7]Oh4]߯V-͆OtSӇV35#Qzk9Ľba5@,xu<Ӊ&a_/W|j`s(1*2]p8p9ɳD8OmY]U*9׸cFvY(2T`3ZesQٓMgb ~g$Apeȯ|j툚$篱H}2yYDcy5S"[C:>vֿԊme_waHL {Ӯե⢐YY)l4RVI'']%UHu!W?y ޞАJ tPzqӶ)8 rʧv^$RɼC۽#\#]+c앹ft%@bFV:lOxB-sl`m̞SU!WZnyH;8׋hbʡha, P9ҼIw 7t ܖ_Q7$O.p!y1F L !S\4$cw*0Yr,$u=z"J7sj]{u_ DYD*AOK!3֠di ^]If&d&`烿u|IPU e={WKlThAYS=(xPMޥf%:l t `Fk[f HM鈭M:v–֝<ʿ 'bigD^cbuD, =)IS*ͷXhUIMs ~Xa 4=iFAj op>E:N M{~H5q b?$@"ne;Ӏ U FGݯ7/1TE_7"@TWq#8 >sX%Ivo_~^>(0NRUͳyŅjXYnAD1ㆠI2px}CxBo0XY_5]h(KNs}%-{x[)k:}w]jxfb$YP `RD0Nk4Ϳwk@?op:=Rp@4lYҫKr;"zd+ ([>~ 4U?F@\,!+lS./Tfb9J9VFϥWMnu7ikMT$bzKO Ʈ;_$j"oEtj-h^Ȕ'.U>?CWǭt! ф t܀ARWAH #K4nD[ NWkvߊJ'?Y`GT,Y;'I?:(@}.$|V NvyP(vKoy^є &vyg/;eږ6Fvhg~)E1v sXyҕSzFEo;9μFBr\3k~ u/&{0}.Z,?/ZFD9-cY]n8Y/N2{ b,b˒dmOayitJ.U.Ar`JJ)rMD޲! \!4ev""Y"NMTTkcʧg2b{|Dd0fid(k:o)e"sW T%״hUD}'#>v ݬw٦n)kh-R=zS4L4Z_EPL+ZU&ZO~_ׁϗOhA*./C*:8J^o|Bp#;z5.ҧ wqnetK[|̡ GΏ,ecpe4I7} I*jDۂ(Ƭ҇E!{opZLnj~EGy:_c[ZG8xY endstream endobj 110 0 obj << /Length1 725 /Length2 37749 /Length3 0 /Length 38251 /Filter /FlateDecode >> stream xlspϺ.Nls8۶m;ycsbĶmۚض[kwv}s盛ZdDbv*t \e1FNV3,3Vٔ njP602av@s gjֆ&@#@hlquupurst'Ila 0Z4%rqS[SGCk5 46u20sXGٚɉl]MfhgUS ӫ mM2:;qmjG6:GedF@[X&ikf`]rLLv31P ػ8:dLLmC m!lI njtogGM37&@?jbgk_=3G/"*%D?Eo &c[ o,]Q:; t*}-(!!;w/Z&-3#5vqt4u71e;c 2тrHiUeFЕkmࠅ% n<Ŷ]a)dQ2KIObk]usIj)^q(>H䭸Յ[_Z_{UȳWF/Bswp-B.-5;%rqpD6Cȷ\?c7.wm .<ȇ%ZQ|e)?ؙq}VWסpsݒ% `;^^rqX\[UG"(o`WDF$_99Zo )p^7;gX7=> =imJ/c~yI?0jgkpm:EĺL!>* dhdL\a;S1 ?]E8G.K w?-?|Hix&r iQ/,NP8A8Գxn?z]=W4b 5V:R*C)/㚅<ű{vIg2[nX mA^deK@.|&Ϣy5zO p>O+hzK ]/Qjtz;*+Elo gv $^z F]mc7#H9ѵ@ĄC&kW(eEˎUqu $~O#MFg7r'VC89BWh]vjlnjsJ]b쇇R}vJT>(,DD/cO O 85Ut5P8 ielwG2ZǂP{FrE||H)HǝTkk5L7KMZ(QMu'PB,i J0:IOSŤjl$V&<'vŦ}P^'VЬX{D풽]d 71kuukB-S?۾l#穫Q3GAݦ!YtTƒSQ?ih`$ tjІ}XbZ_-nԡA:^m!" gn iX/$tIw B?uLwP'SzخV㳒 ,m7 O<b&_%sJȌcs mLatH~ ܉(eQ\T%SP-YGc &h_?|PЯ{Tl*& Yԃr{7! Պ8tR]vdf0Ϡ[Ԙ%Qm͕/{~<A֓hJ^hm"~qaTl&+dLS H=Zy_Pu /&a1yj );n@TSIOdwVj'?ĿMqKMF>h3J-4«8۲,/:]oaN/|}$m~sޤ]$X~fT"P'*PʔAeh:Mҕ/;4M -_bGߡc Pcăez\GAb ٘7AnJnB+,G'c1XHmݓFh;< _ d=*19Fc`+q%@NE*fUZb J&>U\J-g\PG&G@L-VFиqZHoc~O)^>^%YAݷרR?d_Ɍ7u5ܹ2=S6;j5\O6Nrͩb™ZƉ>8m0'Fz5-V1xWPyr c5o}sWܲ²U3:>kxK Z!~-5DH'SZvkWH^(e6^٭ށΡj䤨 -U os{f 9yLY7>WWU*<| 롧^l̷=zS,j_4G%)j}m$DP(a5BgCrC8TxqN3!kmAYȈ' bfcGCTKհ> aQS|ts+3X0S6-ќh . EoEpJHQ`%> ݸ\H/ncI8rWqi[D3БqN-s]%k,FM@a9Ȼf?/ubϸ+{X 6o[kuh H`dzsK[SIP#QlLitSԟ6;_ZA7L1[+Tձ㜆XH6F5[jT0(B>{Y,M|ǠaA`I ZY +<ɌÀ}h+!\ qjLvceV۶ >իmV",R-(ޡP989^n"`H3T3 6 xs{FxP8 l IY=玛.]q݄_ulD&&^(&籅:-_X?жOU[&U;ѷ_jXFq\g !g8SjpYx&9h|FFA.D_.cku 0H.dwo"!#(碌݁C)lݹHl䄇Ԥ"(={[HiƟd\t}|XY2 Rۅ^/4o̫'cNj#7 }hEpݥm`'6X/O5&/ 5BI_,ܧ $hiWl.F*j 3Ym%!oD *OYk]bLA-gˉӋ}gί\rvCL<| 5iv xbD򟄾E8M.C*<0f>:y&T{Sךum$Жur(nؑHe-#7Lvr CnuLCL`xÓ,p!,ŵ1p.t ۈV:6^^WX&ˆ`so*KRNQETީk'=TLM d"xP $?z)Ϛѭ =bj$7S*\B qCBa6zW͓g/]`Jԡ0RjJ/I'%¾XoJ'Dlʙ9MAKXdKD2R*.xY7֪g2yk(Sb* +sLJfP8?01au4jyxxWɐ5핆0M v,wn`hG];I09y):Erd€KR"ErX#PjyFp2&#hZ1/r1g ;LZ^hp!oGqfDJV̀Hr[3jם1=,_pJ^í.28H`˼:T>tA}fFѷsy+mJ1b&hXwB=KY2Z-pDk,.=q ޤ?y8Wd׌{hQٺEp}:k?_"bUUÔAi7w"IWmL'{+pD;mr}J+,ӚW,8OXfM^rYQqlO|ʲQ>L uXVpgQE( @OtO>8R, #D$[>*> Bh:7z]Ԭ+H_ o}qsa^6y-a2c)4З0b {WUxtO+Tj b!SXeG+hJ:?Yt ]DfhZV!  D{4f_:UoűU!-Qў!L`S4"wg=F Ɲ#}tRC *bVh0xp}n%*$v .]LW\BO2T֪r|}3k_p\7q0\ m,͘^=M;e ~^MK Bd~fkI9!D,k):3JF&q0ݶԞTSJR??X/8tmt|lGZK?IvxAo_|Y>3-tPX鐶;^*A|g;D*I/"K&RBTxJE]xIOlBtLźcJ̓$#XLE{j]y⧍#rmqHV4xxcm' Rg"ߐO h8T:wB〗tI850qȥ-*'B魾d~lI Bl)5jd]+}l̂b ۀWއ* B(?ϔm?ᐺB[%@ 2BhLZ/cp\}' ֜5Af}fj 3止p\Yy?jr ?PNJU5FeY1HL%oFiL3z:ժ8k,C\ۤහ{YPm|OGPS+Xɦ$(M4C90bЗcTKL=yU^ 9Zj`~ÀTw}Yu{ P "CI3NjtжI]qy==͑*et"z[%`X^B[7:EׇpՑνE)QK@uhlZ,^WC/CwIuL&ʌ*{$OqK)vUIj;$1-N \$Jw~ؾ)231KwAyl|Q!9n{i5Tm7Gtb/b&|eqYp4T_*xTVLH7@}xҬ8>>WN $Qq>՜a~w' 0E0H*,!t-hq1=1%6Or2j[{@{TiWJә/mN ⹭u7 F =mPLЈun޺N=z_YV*NR5F Qt3I |c`[&RPdHUuRCJ%Tj'+xX  D9~AwQDmM GUmcR`G;d kt4C߇Dr~ߍl?QY0pX⧜mh=@BRSCsJsȒ>9DžDVvqd)ptn(c%ƫi2V9ҠJ'goډn}˗Ӌhst;}"9)1d/ȁH4U5nYNڂ_>TWK#izSV<#0[D!SϲXLȼH!vW̓GZ B*p"c4Fؗ-^jKBeY ABΕxmEܟ Xώw}YV%!8ݘ\,BԜn>J zBn}pefH=zdOĂU [ 1=<"Siix|Շ@Џ@X 퓎s36kj}"R%&vQ1Vch KLՆp%+Єf sѥ,:\hGM7y/MA3}#旎&Lgt;\Hy*J+<16Tj*,u],]3= V6 F㣮LmwY6ܽזt^q62~'Ɓ@WRճ%@!V x &~PjM$\\zlozcĂB\ >n;U] p04D SiOC³10P 4ll^*En4hϤ׮ɣԯ]RY犝^;47-L/PeXNPBVfajp٪Vų3@#G'q}9#̧9˃q#^^aۊe-?:TFsf Xϵq[x{qސ®` rysrJUN_H5pEP亞wF'5u魵Q6rY3ZN:wJ}ۚbUܴ#3+ ~^q .5?>?'xzhzT{PXs3n(*։,ٿQn_1IDFS(`18h,X@>Fh-(@]&2EmYh/GI} dM6ft4 K|fw^Eg [TBė\a!du']EnX=o(Ur*=b,k%`vU%naJzAf_)+g]NO:68 Q#P4"RuppCǬfp^\]iR[%1G1(@Z3{mOAK<}GX6<`6Z:6Z^];9O ȏPf@h>*+ʝ-@e<.+Br5_58 Y~]; 9nCkIkVѳo"'DT ԅY$P%KϻBDФJ;`03]|x0[p/8KYd,ck?0t;Q~H3hr̠o]wQ/_̈́Պ)t{Ƙ?5R7'9ДZiRp)*ZU呰i}Ҁ1\kx^zڳ]SޤŰ&pe85l)粮}k(6 o`}:XerJiwjX0P\]t,8RG+> ӪaQ/ $n*6.pJiT-:/Ac+ÀFu9jÆyGJkhO>`9bbCBk a>ʼ'dizz얷G0::D6 b6JR_ ;퐳.~9{!{:Ta,OQl e0x+^fRL[Q (9@ql8s:~rF*>0Ӎ* ޣ@"ʥ OFR::ew讍:$d[9~94'!#=mߺWA#*5uXux!q\q ,fcڰ4u;rzyDwe3R-wgs]M9w<'oŋj3#^/bW,a=d-]72$P"M% b3 "8V8t 6:-7e% ))g*FɃ:7wg#Ҙا҇j1A?i1pMsڷC4|׬*&;3HEzOK rxG{..wN7BwOɽS'mQEpw mmgNڃ<;i-5"qZǧ^; D!wN'3IUzə xra"K6i7bײhVqQzӳ0^4U9?]4:c>h%e80Hy>NM~?Lzu7̓XwhdNDA&#7wXF0$#!OyY!y+?j I||w[^ 57]y0`8xBo"z5oe*y'{O  )j&R_pFfI;-eNo*,N4n"SȆ?χ5g O;I}R%CciD 񗺡 g~om$Z<%ume/lۉqG+ȅK% N@U*Z=1Uj&ƍA ]T(BgӿKUts|3.+@Yc@eFdt!!Y Gz.▣O3tm֗\:9$Yx*Uh\l ӧ_?l-췧*$Hm .CWvU3R @ڀ,A* 2?Cl:PA}Kq gX9uA ^,oSD ;,ub$mX]Rh{ Z6 a>qyc43fޖp? f7#P*HMȒp"7w,bX)KFO.Fo6E.{Igs 4y2FWrQVk,5}+;[fbSᣟV G=`X1eMOȇdVئ 0"udzoT!.߇W-{6 JY+EywZx~Η5,:Lo['Q?y"ŕL½Ed( Z~ ʇ6u78주TߤuؾOS}ao J<斆אwO(agǨ7ocȒs: "USDaFibF5:`w'&{2ϟk+ *-Zw Tk*&;LtSɇi" ƺ,˖J{Le}wsT~&9[.dniqjjHsC A;l ]!xa*/GL#3ǐRϺ Ve џ7C!"M+OMqo9IF6@7NɦwlTq(":ABu%ş2N团ucy̞q9RVo;ȐmƐR*mZ0&.5ᤌA<ֿ ST]ͪke.{ptM q@0 ^ ^(,&/lt4~y !gd:WJQ===-2ӻ!Jܩ($&\A^ES[ M]tgǸÖ0 UTȈYU_+F:@m&$yku5qAQB"x4Ӽo5~k̞{joA^֨>,?jۧy+֌f|Q{35v/ u5bѲJ1 ђV!0(p1CYp)dFxҜp*^mZ|fo*Yo58?G*-M0^U* 6p^/ Xty ^8t/K Z YdkNK_jF\?x8s;( %-lݏ[=( ?rŏvc𖨕YJ~X.V¶1(z&xHpF1\~Ep7#g\eegݕ4f pUSXugt 0W6еW{OBS-(0/d RFR}Ă4=`g~4)`ߎF~j06ϕU *rcx,{ߟoCꢮ8L5 C<;g;F<Z?S5Nqp^WrjI1Q2ikЬ9h(~XKT0!Sgu l̫GA2D3B e,~_\5$C fѩx{͙]?L ,7eGIڰ:U{8oSwH8p `!m66E2wwmkpdi77(gQ ;1wO@{Q(ϊ%W5TPnh|O%΄{19BP{izZ[x 釲^kҀ+ nX>N#E:*X \,VOXU p:D@VYI8z vG׌HTؒ^LS(0(ve{q.ɫٖs);/Umc)7dmzkʹx1 Ly[,H8 Y[,.?K /h=(LjN޼*]0^cΰϞ/=wƎ!KאefY !lpG>ٳ.-2AEjDL2l,Z8jG-uiկ^'d9zNM637'lr]LJsUWt*#)FGVisNGa ?)6D?fϖƨx$s?e(?:PuWLh(` iVǤv[F;}`Z"2.c 5]:H؅?驰I/Dן]}3-޿=ӿ;Fќϡ%0U>6=U ֹM ޾s. T:׏[2+;U !nO! >Y5Bb8nr=I,H hzRDHEF*!S?yWi-f'^GI*H j:G ~T0W;J? ͠2a(σM;,cB$0m 4^ FKBs8Wv ?n*ښN|C֟gi,H>vEFZg'7 AeғJ뉻KCݲZh O`:3wA-IH^Gmc4mb=ݞ5NA $rͭn.:+lGN_N67lFJ<,WYqSq)e1ot*Jh7oKd>OX/ ~"F8'^Ttw!Rf&؛j[zPUgW}gunTwWsR⽈F 5i2 `.5j?Tz*(r cNJeAa*SYp9KIgh&J a,,ѐœcT EM@P"F5Ic88Cx[@+e2$Ax^T+_$|~% $Ӵ܉%^Vo Ӻŕ,}?^a &FMy%yйЇVպ(]/#q h?nSVV-j$/ Ei*K$)x%A/jScUeمS"fJb?T< ыP?< +Ws|6c13%C{O5'%ec69}G=118.h+\MfU3P5x_b/ Y$" EM݅ CʑrÙtVq:|!LEg=WQ.^h_jlSq*aSe+]nmd W <~ˋ%,YnP/ 4zbnwl/-xfx֋UYP",`:4 g`ZڧqQ#,S.#.u>:7ΚA<@'ʀ4I-ZIjkFC ͩ0EV|ɤO#ik-Qz&ӭ;And{>Nv44ƕbаDlk1"Ӂ0R0JmIHvAf3`j_|aaknyWmWowmrpcRl"[6%tǮ<֏Jؾ)Gu'E{QǴ#ǍdKDt/^ ׁ;("IKUcY )b0ʁȪE*1<^ݰQOsOH ?.4q9h.r550FRwj[w !? RZ3s$r-q);6]haNJb {>ו`<~M[[Ic3W)ȃW= GlY&& 7DHؑ=N;1)SP߈#  'gK]D Th/iⲍټΫuߣ uKAk#tG[6&]XAj%vK {6 k{P{~L~Y/c@(}Ë(҇ѽI>i؇N &'<5 F"8N(л $U'Bɝ|c 5eP1~y׫$_#*fAfE-,O$*%J.)Dxq7-ҝMƋQT64IF 䰉$k-1c)}؏ N#ф.Tc&EB/7HXHsYJp捥g%{, dB0yOɡ֎&lTA+9;y dadt]9;;,rS{5* RqS9_ےjayY1Ms9$KE-DGcWYIud&4IKO/J% pEQ BȟjhG,VaݎYII/mpj$(cGC6D3*X7Hk=sU4Lܻ> S3Jp_&ғnɍC>C|_;C~&?.%ZLe$0 @}50/69n X e6ba(Nׯovփ9t|h]Y"l$`BHDL+F ܌0HwPv{#Ȍ٭I-VZذhUXTȲ[V7Sue;˕=`õ.1ki›@?_7>>,siH*u|j1v뻺 ^TD>K>~sgx.-D9ej2s3 =3ifY)Uf1{N9ۊ'ÙZک9@b)?T9ύ )Bɴ(ٰ'Y3ѭx0ˊ:UXw %Z%yͺ3ho5N\(3e.&xԩx:< [j{p[nl^?V$C48BV'} )DiaumsU^(E, ˠ2pȱJ΢f!@g#w8hah@ (2oi/;],XU+/x(jc؄.? %\ t˾|`( mͅa.`}=Ew4>lP'΁1uZԅ܌D/1@핰ԉ$]%2c_k#ptnkZe1ۄ- Wzҵ8Yo,@t -->qu"p޻jP`]yTd.3? 8f bˆ՛9ypSUxN'#ar7jOM * ðA; 4 m1:R7X+&:ND ƱkJ`rfͲ%ߊZ D㺭㿬V`ez絡C0s0Zam@ҙ""NYAR4c<ˋ v0guOgZ8 ղ/~]br۞صа:N`3UÉh2!']&Btx̡dv!kgH#BJ$= [q:X׉٩UB@YXQo.{~f4+A 8> O+pfT*R&>uh2*qfQĕZ~.UJ A%P虃{_YkUp.hF2X?̯|I:cbјVg[R~9Z@5IgQᡗz8 k~}MUf@>=a׳ (A% DDq2S^aNr5xxÙqX ,ጿ#6 XP:DX\gTFj+0E }`[B3N1 {N# z'R+˝;N2OWp#r5`J( 6euuU/#Wյ&\)[ӊ(zEǿ j4pꍚ VO>nm^3M""네l~y%s+< pS+=Y~\<'RU@ k&]!pla:Cm&v1ACkcOY<9G1_2yNjZ`0QZƣbNݥf<7㕉,#汿3 Crvw“)nyt"iڊR,YԤtbltG5gA Fvs.ۑtÿSRT)i&,8$=bcR_akM9IkkAui Zg+$݌L軣e?)%y 8.aseta,bcZ -}ϓ1n6q3h;sw0"AqIRJe#vIagX3zg=9`_g;5YJФ^⑦9Dw?FQ- /DƢ1á֡#*q69bԵIBi8yAQ84%X]\N[ ᵢmM+χg\@ZkU̲Kϭ75964ɛ+9Fl{Gb" ֯_-6_:/+GbObʤޮ@\r<(eT/Lmc/K"GNPf>¿ܬ HM ^0R|ϳ{f#2qu+ꥁ4 _s[ F .{9*f1D|󸑧È:tr ¤D Q@Ք?&[TK'@^FxzѿV+ƒ1%}l{"p'&]8uԣj. oؑN7A|X3`sN>6T T!vy^j`4Ef?"kIo&Yv' uÖ2*c[}GH8M ; "K2m|wsecjp+P>>{qZ Wg o- *I%WZxQ).Yوt@NNx}-B[a`nhK#Zk͖Haf*#Jh]Х¡)ۛˀ4)ԾX%@ *p e{/UרuZm}Гф N>9.6^g%a]6SpAwȪA'sJ$RR 2}a8() ;1US\ 32v a')`epgd̞ȸ/Om[ m:|+GåHufzUd\di!ARљ h3Tݢ5HA+9Mh .Ñ[vCF R-s92dX)|L[Wa- Xit7k:ҫKǵxV$/{//Xd¥5t}Vf|0 Tqo:.` ~׈LJEk9:vU2PPdG5-5 , Drua)JhE-&4V ,rne!GXrµ// ?.ë b=} DPԭfzthBC7[At\*߹ AŖҘOzR+.JxMcKC7\c"j".Rp7ȁ9?ؐ=޲?8мɾ2YV!)ඟt7C8uNa KYŪI+S7 vJ9A7Iq\@8\BsCn#6ah8d'<1l<./nS y&8"B -TOrkXjh(;Q@uU9K2 Eq:֮]A a$tɤ; aG#j {y?P/pI@LS*z2|Vk:_*AR9KJ=(nϷ݋ Z9d*Ɋ$G˴4}'fFߧ_t<)K'By{4ҏ75d7H52Βg^۪ʓ^_@p /-](/KU='1Z ъ9/g+$1yLב# Z ?\ի1\]7~hYAnӐbDg=I͗ق'!k)qZ dkZ_ n28KT<$7zPsqu@maǖτ^8iHyga/r3Վy=e B,Z1 4f+8T="*:zs<ώ}Wh;g!4՛zހ(^t7Cc&o#W4ABUޙRfFodoW`hu+ȧO:;VUO7djSVP VCR{;dH&|˚}Veď$% ݾXݢeL"tii{$b+!-t4>-#ZѶ=ER&{$͋(JS7?P-DK{¯1۱ rW6d2F "AdGU+KSWEk}) Vh gJn-\l<-wn\2Gq{5:|p'X!^yK̪%f-q;U *صP>=JӕkiV(Ό\G-ɿZ]q6rg? 2`GURSg7iMm;Iom(jm ro:AbYbǰKgzXh*7g~fUIjD2ph L^++sB Jq܅ȫ.:  ѻ~ l0K4+v 1ipD4dϘ?t@nw8[sgnTG; V:`"qv7 v]%{^h/H/OtxUVf^Þ :[|y0NpV&l%_ )']Ō4+`BZ`ۡ{HHHf`UܭxxGP/';Í'K=CFt!ykg]r֏C$̽#CDwP @գ/lL`CqqV2 . 眀֖OG;pv^~QfV/s%A } g eC[T>vX4`g{3(i; ֖[n> p6Q (`]h/J?oZEACf @b02y!YHx^<|=HX&DfP􃰤"?bؑ7D'BVVÀtєRXc\M>m!\muEl\ jl2D[43GܡBN5=s'$^ADU8=Ms[꫉w( ŶrӦrK8ABS^P7k.k0{n|¼(kA6T4~Nz2zN^sPd3;T5ș ![X$ Ib~ +,@0Z>i+ϓa*+K~g,IՋ|tN?ZrgU]ey9}(S%Q2<T

xV1? Z~Gi^iabԍ^w_q9% ׺U5J++1jd@3x{wPT2P/n2׳el±XP9=bSS.ێ7E~CF,ם)N/6E~ے] @*HxU&˾9%IZJ~V>2xicT]NLZ Ilt0bXnTbEבQAdðe$kSgC7*>-V3NQodȸ;w}͔8u+L_Hd2 nG hT0e`b>ie8P&OySB7 y!-xXd2:8RL3{Ru 漢8Et i9Gjj-KNۡyv(DLAJ0w޶wp(H'ƒ2}P^x$'\lS] ѓ wzAo_~>)(=؈w@8X'W,$v2w2xF6Nj<r;u!YҞ,Q?/H-GC;5{A7be켝O STvPOM/[_*~&Z̵I2SDf$};9 [[M|`vJR2~o̅kVp ?~ڐi)&8FXCHF/K()D0Wt>ɀ4do%ҠXFbW␂@ ];}'"?ߌkm$2bU"){#*Sg+[ hI_̙hF#Èb_AbpcS49o<mʄ1 ԧJ/˦1)#`OI|\Bd",|0[ ʏs ~~HL@9[<XuG0OͽV_a2t ч??}^Wk3{4&cij+^GD̀PZ0Т{@=hc+EPUqdӣ!KѦz֢~7`_77P]Mf9Lx F1bqLu$-Wm?Ʀ#`δ(Jc7;0@]\Q>83]5j>)S.dɦ5;gryrCh;wrfE6(BKdC$K 9.`>>oy˪HUqf dr+YFEQ&Z@ nI \_NP|Oͨ2cKҼ> finTMYn[;Vʑ@){( 9̀+܌죐}X,GxF?:Xpt"dT"EG>Nq);ȉFZA|p$8w00oJin(v/{hF#,.+0'#!~)jCY g`c,uC'x`VO-C[@ UH8$W{8µLQS7Ү:|G=Вѫt}A V PIix͚Us= 0i^CzO$ԑ UP(84m.NSjbc@J"!@ x-u4+!K֮  ixK|c7ouN,;*zV98 iWyED9 Ƴ(|=ٷ)iF >BZKXIjUމmBp(O\BSwlXZXHgXa}S nۤu|IHXmX|yښ:w S$qÍf ݔ@? Qs-, B1ZqXԑrS( >`--ԢU;"vsL}E푷xVmbr=謣A}OȤn+b\'8ӈ*hU% ظ_ ]APKO}ۀe"qJd?+vHsd-L p*(%MugmV>]Ք$~yZԟZ0iT! cG)S6\hUcɭK giB }. C.6s[Gk֖j$wԥ*8QM5nϡewЀrE F8ivEk$0G`clYauzk!S&d*16/b{V8 uAF{IkRi :1SPw LX!jNB13W7yه, Hֶwt'Cs깥1"ت }-A+UAoZ;MHt~X##r ՘#v.* f%p*s Sa&Z{TĢuR}D,\4^m 7n@ww|F`䊀^}֍hPc_sz4?}V7-ZrBĒh;A$qٜb^{ʣH2͍ΔH/Mj-@Pn:.8 ,,_c.n$J[ʬC\v6x^Fj#"To\69|_;lRZN2 Wxp^ 69eZB)S% ?TjQgݿ7 "QS)R#I;ʢ__ei3p :)ɑ~ )J'!b?ę|#=!5K2" `{p74UAtxN?, -[ExL#ROy=5t<2hQ % T?S׋˻Fa,ґlJyk9vCBhMzA% NDPm/<٤Zkk):2+)Uw:ɔj1 6aᑊFn׻51rIxyP\as{ Ca`0V G̰y A͌ Et+ J L^OjqT8Jyp޳1 8,)ډK{mb6_<; pRz&6XoˣدWG9R?lIim5 i3RX޺,?̠~\ [=cK_ -lMO hp.!}i2)kdh 9pPhJi]iT':O[宖p72sUGڱbf1kI,*A`>CR6Dx]冞J>:Xc킝"Ns&{X*Z{% U^#-62*rBmg0J\´t^\OKY㤸>%q4*Tui_DXrUϴv lD_[{>ۮq:k{1Y3静9&wD8~])v?ݭ/ lSrԅxRU4?[똿Miƒ~YWL9H7`&E*.}d|)C_*k,5 B,dE-~2{?.nT @ Lf蚗*NA|]/fz Piˁ| O,.AI p*.,֍{y)wkK }h!4]z$ NTdJCFQiѕJ\RD>="ZOzU|:3{krMV`WN`U4)#U-E+JX`^wmśƘ|tLh\=V`&}v /^]ɕ{Q/*kH)\1Fo(j~m뿞Gz͍(:7k7+v"۫ پ=xC-\zFF⭫ew~ep>T2k5;G ROxNdu +zH~\J;+Y4ISx( tLq}vAR{MVz@-#8۟C&y+ck3.I\h$Q&a%KQ{ͽ.ԖSq- i er*8;fd8Hģ:ZX6RiΜOϟT?OrhAISaS':+U>>vkK@R ߿iX" HV<ў<9+$wE[Ֆ$ch$ ԗ_zE8_L T5ɎJ,̠(b(*J0*իYi|l>1㑄IxbOk>'3 V?pg-/!#j,^nj#1MǾ{Crϭn`.CjΫ]ť=bX}׶_܏)`GQ#eK/. u`ZQD ?7/ѐ(ӆR˗nE " =Q(Ern|_0;.@ą8 -V1}ICY6T RSGqP}yP|k>F4\۔<.s PZi*U3V"Bb nRgkp<5o3l9˥.A[ciRžRiC @dSI"1mKÓ"gaG[6pؽLx/&SJn@m?$ä)ɩ/#GG1#! *+Rj=cW&wԕ/|<{rWf\AmuLz)x?#&՜+6AXQ`sK EHAǦ$r&& P<7F7i~="b唈z>'ضnXP nx|m54ݍnUgjr5|a-hZe^ٟw  +)ML9Z:iyKAY{Y򏙹85:tdhVP, ]N'9×0Q *O$U쵊ӭc`ò--2֙-,~"0^_Y! Y31iЮ(8B50#ɖGIyc (EA5\`̫ݏVM LrhS(rB%ԓsbUo!'&ux1 A:W]޾E/YƓ#nJowdt4wb4 uh%[DWz.+WB$F?aZ,Ϡ@bN.y2$ w`6w^R[+rQ^pg' @љ8qv4.5D#'84$)[2hlwUAg7#)X. ,zL%Zv~+bƓMѳ# ?+[/P`<{1/0IXogxcxH[p+8_b ?}<Ӏęa;;9g 1^&QLp AXh|;YzU:t2EiBws}L|7&=*B2hB$4&h}qU['Om)%<WD\UZEDX c߯.\[E`fByKqZ/xy2Qu838\xd,/\ {Ml_p_V.+肇-3\ffB6iViV3ۄ]I(AOT2@:kБ_%DD݃B(rR惓)q%APO;wy\Oqf1A;3ݥ?"*]M%CºڼRH4Rxȋ&_v#%m!aEoy/, lyW{Ĝb/:i)g86np(Cԝ|dP\%BaNUiyr^mJIMsȆԍ{! 'OttNs!w;$B.F)CO6 hOcӮ6XԱ^cO[rn;=4T3F's]YH='xċ.Aоs5@'R g>VC{Q#.[r ̚"f1*kn30^>o-&OZI)EQ5IL4g`̉.iA~Kr,w-_!0^ .bu[ v=tDOgw~Xa@ze-k?3Q~R[Y%2Ј~=#JvDbGסN# g4-"d9EDK;TtQ6B` S1x+ȏD oN(1Iچf0?ZZQ i6Zԙ$+kEX \`t{3{tʣb^fuJCićgsur8N`YgqIQex88` 8 kU7v)rʨ.ܴkeVMbFW{6&A{!s>K8Q̻V^ m~wӓAp :.29_s}3m iX;u<6?r A vS?)YTG&'1 Xڭ2'`Cc{+7 ?MϿQKs}tX;qV{M4;E$"޼Т*X0[StG]F"(Kp<2Yl&3I@N&kkKLdIT(q巁 O1=`BƓk-ZBٳя arVcb!]y7lb& XMaV^ ESgtjc91*cjx;BrW/_*0O4r}IlsL ihCuF6EgJfA $h>w Du8 Vw>׿M?[E16cѠimDd!,S|} T"2Rhg3z sU8u/ )Ĺ7雞tNBD^qSlNk@H]~i:A֌N E ;Xǣֱ(V@eRv!{kIek:,X5,9ߎ֣br9}y=AT~O.;ȕBQ񚏆j|  S&^vG4B;] hVs=0;X,p_Bh}TDRqd(L8U]b!J, $ 'D?k "m`<,5+}u3+;#ճnNV$[a`IٲXKs Ot8\nI7q3n/IjpG Cq ሶPNiO¼K ~hrc0P4oͩjJ3ciz&G}НK|mA"uCd[VjNLCx/5Ɨi'ipʐv.EۘYa Q1I"uX;RR?K֠mi3 j qYjr>($zsR?5m3df8uS\FYJH!h7cЃx͵,of c[=|4{pm8^*ïxxO4Bg,)3;/.4i"L!O>mɬs[(iΠ AtY > stream xlstf-vضmc?ձm;ضmꎝwιUk5j֬vQcSH:ػ{:YXyj,fF68 1g+hP:XX@X(bVjS;fVvVM[w+SK ?Ij@ 0ĔeR)=fbke 2ڻi fVo@g;;$E$bLbc{3?.Z=,[L,F8+hae/dq97!ѐ`4fkhlP99 f@g{lPc;+[_Aȸ#?1e"i4SrGoWg7?ҁhϮ9zYʿcVR"wXfwX?_.(+:[ytU"zk_7+3+f_tMݜwb=p|!a~EtFt,k37VÖfWxwx?M/dZHS1$x/%d~;o* v7]377iٳkePXb; Qv֗hвtjP-C M^~o#PZV:y0D=Ƽ%` yIpkԥL(kX"J=FVGɹqn<U!d@agq ̨r,(J7>k[Ϯo3OK퐴Z}d~CciTeSi zB(; cAsoNB(CQ)k7S}^|5Y2SG>&ԭ棰y]1 @Ȁln2 / ^sN2#|eýٰ= WE. ޣYS:6+JA0.b2W)X~Gk'^Q ŀ(#ܗHbjؚ;4okO<7{ǚmCRvgDۓ#Ԍ( nbD(\NoZ[oH2ړ?,,8MB v3|^4tC%Bm [e zNRFê0QR̝a%ܕۥ $aޝ"NㅩbOoWȦu{ [H7 [_u| 0G%Y ɍXUtb|yKه0ًG Kr{)&ܮRr`I Y*liGF#O_|yӛ t /LG__޵Jh PDo9/SN4GQM@Q:"Ce:$XsP'r[ԛtxSN Ue&mdpSU _wK1H~ .o`6_l~S64B=oy ;;&m["qhR'҅ kA./^TTe0\S;jhE"ekX:%g[.-9L]5kJٜcZsje.ɽ ;Bt0:ů|E6'~xLz0k) 3deʽY/{Q]Yh(?ZՌ|zygqW{CEkV%! ͝%+Y I PFWgNjNQ+%5G^$+9%J%^V!.NLG-3NcwMD`gt[ILUQos449Ẃ빑85`ee"{7EoK5zR]C]0tL5 E; ,y.(32$YKlcQҊi\lTftvTBR'ΧY|7J֬GxUT;GlMT)-Z.Mx3B8/u;<Ƴqb~R234- zk3p$Rw XB:shI|ٞK.,|х, PCnsLH]4roE Rς@%Ёh8$O~7YmkBԳsGtƗg)&*o[A䎁ֳ F:>OmUl#4vs鐭Tk1G0 M%%F<qa_@N WIaT:Tt .Yy(BAM{!lE b1sšE: 8nBﮩHn'b|H@*`Zi|n:wa3R'KD4gLʺ4Y`ԏ\|, SHi9=G:dP+.HطNfj`O3cZwOCUn0bihmiT*}r#B/71bdYp #z>j.߲Iծ|KLkP})ۓÇBaK,@ThH`QDi{aa<"ɵ֖%!cwѶY&4bZjdHI/Ԉ!ɠ,8QAG[ug]05}ҌP/ C񰆘?P@nEfr(x,ۋ3^E@/)FECґsZۻa, \@@%Pv|0Wpgؘ d|K#7}nO ~Jܠڪ=ïIXh! v㟩&׶!KDBj2N!jvq- | 'zT4;VDY57RzY 7t] V9͝<tYm(^Z:G,UT[1}֏v\*K߇y;qTԯ*^L*7/$*'Qjҳ s6sș-[gFτ,aFvRH,Lj7]ϲ6y&:&y|# # rz6kcBy`CWަqQ'=WN.k6s%O+Cjɮg6 wZȏ}P SJ}8 EV5n|PEDaL\] w"s㆟GtA_`LrY|_ܑq|{V|%^:fwAI:&Agtޞ=]LҷT7+N_^zt)dZCHJ ܤT~|Adqgu}GI2@ΎfZ(G &H4I4:)[BӛU9C`Mzu`K鈶$4DBlU4v{?M?\ǧeF}ԭѥ 3ӻUg[u":)Xw&ح:L[ol[C"!Po״-ꋌ?c7x20/?HT\Wkxr3Jn" Y:>?/Ֆ-:ntJ{ֱ!]Z?lelޣjH,]{|fZ0$uSWQ]-X'maƒja!R`l߮D9OIWN Zx d5Xk-lŁn/;W4>gYuy|zTߜP(r<[6> e"p"O46fo6j7?!N?V~WyZ}m˛hT|u|\44aE uc`3~!nWV8Ǝ$ZJ3fb!oKa Nנ#] 1/S4-9[1"}d0CHNS8ȐMҝZH"?t|[cK_UDMKgdN=)\o|`gͩ%,wj(}cDV"$fܮgJ 3RގȞ4 gݲ =pVoHp1H둣kуb5X4U`?\R_;\M!Oip"|ť8buQPZ9OI\8^d-poB"-N\ 2օnl#ͧqr̟Z HKO֞5则Wt0o~g$QVpZ"UHhq5(}~_& m1eP4e8E9ZOoy4|a9~ ^/F:-u\<ɩ)Fm.=!^ޜ6cx>vePt"GmtHֱ3QĎ;#:R x_+91l]|TlӕWyėD)Qf(7["+') k˙X:pC)7 r0;ҡ-ǑDVe+v̅;dɌ}uZtY 6]ӪP#I'$b߯kPK"Zdb'+xŖYEq^Ju=p櫷Su=fa9\z,ga6 ,QRYP,j̙+catnau6; d%+-D<:!fOYE3JMˑjh}3=yxF.}(0 }AS)9hcN١jKݻ؉wdqNyKغ3긩)Z*FUTz|'=Z/(LuS y<l9"OlѹɢCM Pi/LqkD<6H+*tKWO3P0*sAܜtD|(ׄz:)W4T'G<1Y<$ʀq44Ni2՛ sgFͷjˡWޫޔK8ѿ&ťV)qike *IL->JRfmc=R%V+uA1FPෞҰ֫ ^3K .=tjBZe>:CM2:!4pUaWm'rx[:w*=4+5 ^q^\/Q ;:Nxr5 Tp<ҮQ(.ǦtZ=Nlh^ؘ(q\ C覽QlqO%gUngD:CYzAE8XuZ-ĥq(~}z= g)똷D`,!v<O*6cm;溌~:B1F>MÏ3oj{PrvXA>kUFhQ{AciņetlB+佴’Ϗ<5&R?Ck)N'U^*Iq[V )ZJ[ٮڗ!MA TETM׬KHW7EXkQhΈ%@jS[>[Sf47Wթa#g1F +EPV-V=rM$ɞk/*\9Q-V ~F@UP QJR<3Uͮ``"҈;O&9A')<7 m1 Uw˪4>ndL/'}ķJU VS5ÞBȟ>> JOЎmOћ܈WZƎ ad.9^2q[FĬ'z̢gEi[Ja>fOK JtlJ Lr甝S%վ) kʰPQd%K,av0"ӧH|ci`ai7̸߱H6dc T3 G XIk! Q$ ,B3+巈q.]jOj/ L9V!\4M#X<8,Roicp$D4 uZVe9,W;v!ѐZH5H{zJWМ/+8q$WϷEAPTDzPb  X Qy'^]p~y)Ԉftˬ8CV]zMbhsy,5rpG0; `_ ȕ{.]YMa^47yFMR.xʹԾWHd$͇ay F}RxҰv%(x}ٚU3obç[xo2%5/6M س#=srxgѡ c;{¡P81lSNr% #} [U@97J QRfU\-WN~w}6nd'"% F2+z9]ĿaZgѐPsUvgj3g ؛Eȼ)9p!ި 9򲹔95EN"8^<g/X"H*'j /\Kr{kopurAZ]BBr¥f! Q]ψ%]((P1]:1lj TH k΂vRԓA#Ƭ*fHR (G8.>*.Gy(}=9R7l ,էK^k+wk)v\96&G;?>s-_PQiiu{ČEIٌ1"ND4uC+Jv %ܛaÉ;9B&' 3ѐ_޹}¯Kݣ|98o YlH^] Oq(H1-4/.[%[k3Ɩ=Z@EL>\Ao3(!P1=~]?Q1E?SPԂm /~n1jLٺm,0kQg`?Ys?_ňjP8JqaN`4{YJa#S8 zb 2(;xu(~jxΊx>O]&Z:d"69k/ukiK&yf~Z~_%y .qQDaM-1q(3Gnxعb Ap46-:@FBHIU+]&AS|lbڏGOtOemMlӸpA3:2|a cQ8QQg=VeȇP2:&j"g̍هl7 ҽ0 Y/Mޅ^W}K';\q,:#ypLR<4 Đj\)9ĊWZF)XeQ=xC`ϾV]D|H7×"KrpَSKt~r 55冣_U- ʃ+[mg(i+p]š:9x+n(9[}^o7m馞jhf~='8Rދ㌓7#][/Ӵukd{ذ jA?#iF17:Sנ:)`j4&=k.hóGv l"Q^H~C]ǫlŸ4Gp/oe4֧lF?Nc^"UfZ}F ! Z#  ыU:^g!M 0Οcob]y,H+ [JSR5t-"S)|Chj1 T;ϟ $0x-*T`)GǵgƃgoCoy+pKsk }9R7c:".l9T],Oˤ_9yWF`fF^|3]y;g9[N0 ǻ)NүVH5{x8]2PZ4GZhZt$I(5Tfq㍙*,DNLETabNzyqX9GI#Rux޳.=o..Yb:CP0?3|#\&8W2Vj krՑo:>Re6=#$lFk=2I'!whbU@%ȇB-gpNR΂ǧ9E]Lݪ"3]{ 2-؞_u vhfVb)UC^geb* XWOځ)֞^[O: ,\DT-V2Guy3tdޓ*{Z )y)JQ]Fb'ȉM=*0}B‚>fGw+lD̨] Bgc]5"C̟QC8_/d޾YC5PpcKG9wu:NQ 7JO5^~.R[mZjjW)pk&VC`h%@t2*D@cFKy9#ƀ&S zǘj'UL"zVE "<<=Vk>Ү1"u L#_u2 n~i.*EblZ! J{qT$n/Qa@I٣ڠġֹ8!K m:Fsͭ0J y):{ ޟ9断wt)|*NIwv_&H;Me'3rHT`F:n4)cX[$CY;{??,PUhw;RJ.`А"k&wf߈9R<S0"%ܕxiŪdL!H;F;R-n'ć(^Ǒ ok"U_3Iyӽ<5(túf9ANs+]ton>oVgˮ~`OO, H@a5lE5#㘏OpBOnhwY(^S]+1rWӸ]٦hd;IJ9,?3C%, jUN&VPDRO՜<jn=)*6`#'FVWiĺ/:7 Z֘L}#y3/I‰Y0 eИL21rW]ړzntw'3yz~{տ9u$YׂbA´ &'lܟz1PpBRbz hu~]>D,\(i]^*=O.!kPiݸB֥Z $4b%M; k4px3XV<DyhhTog~ it:Qڙ _ˆ݈0QZ8Ke*+}ݑ_g!q}80 YUP۟ 謸B pbW~%6dqGPsڨMPAc:]DfC8z 4Pz-GDh=k.5m^imDy˃@̇Ԅ6<=sXYl2\~V"^֐7Eep}l5Ӡ)CT1(߇WuTPN/ KQz< A5?U^m"DYb.ȏ0U{(''qs0c'oQފS<ק~tvP #(Zր$K#n(3+Q.OJ}Z,o.5 q?b>!H}]GܘR+A"aXtN$DycBlRo+k`7As96n3.F%]Uf uS01Abqòғz02>V[E'%Ù d pRUt3 lÛaapTW]Q*Q^}σU.@9pcOpN.дBkKu:~ h9T3k.+np踯Aߎ oo蠤硈GwVssQbloك8Z }HH8[ܠ-_E2]t;![HkD gf :+PΠ:#*?\ŦKB 3UyE@4ʙ}O~qFb&v㹧X?&P/@>'x vfz3͙"roX?xZG\+YjS1=~ʟ\IU DS҇>LN- $hmoL- 52jpWg>ͤWVj4$\C堄qN* nn +e/M-͜v+y1+7,kR#\EU-蔾:aAF6q/1>t3ׇ{?EsE ݳX>w3IR,Pz@i3)Ԁi$,U|ځ;J$iޑ(hͬb5a"tƇyqU;\Rc_υnMO>t]+xs(:qjͻO%uɰMqU6``n'"F@עzmw4}n*;"OdH3K]VpӴi6Fǹ$V<4+B]a&֯JY<2 M Iv7R rNoOƸdP* LUt|x3? NJ4CiMJGMk_@,%WPzht)`(-}g7Fē}ʯ<0aLZBn5k^d޵v=h݊Q$/ΟcL8k8*O™\F86;'l zIžeCn(m%Z==;3 FJ*{3 24)~!d>YSfxSNy m/+m* ء4FuM> xynp̐F6L,*H!5_P'?DSɕwF#lU>!4evmu#"W"] ޺{dzeyIA C騝D鄼Uba#xcv 2dS7U݃@8 ywnޥq?sr7^wݜUȁ34YHKCyQJmB[}Ӑ@MaPE<VgcFlK-'l.XqiѠ- IJK0)7"(  c*kc;ѣK;%3ۼ~Cv0nqSTazI%JSygQdn}~_3dSjrYZT>XPD\JReM'@;Cl!;;u:&'Ym<YR atSH+H1=Jg`t}QIw6e- B[4cbbD+jFDsc>C_4[xI?,BL[F$˗2EΆų:t 7]{j0u{`=,E{N)`AZu oC""Y'Mv;ǽ3}&QwkȀygkBu"7աcaMq e15 s?Fxϖ[=vN )*q!M܆W۶j2!nk̈́OtBWT⼃۲,ja"'6Q)JYѪv|4cGnBM33mC2 lt =hMm[5s֧̑g>*>T\gL<­O|sl΄ll۶&m۶m۶Ķmsb;ުwE ٨⼭t4bM 5l/{(f?^@tdZJ{ZRZ zAg%2B!M#6b|rwS u#3lh,mÄ o4{~aD 23ژGOpx\8Ȅ"[t͟gvgMG,BS]v,'X| P&[bܨ4D){>,Pڙ\J .b?-[]>|)SPݦ,jWƚ6nTL$r ڵooAŖw_ĦPi!,2 hQ)R9i? {!Wg"6n5im[tv>ߌښqZck^H wX%OI 5),<F{2y.@u.Á]Nueް*ٯwV\چq`1iE8U}οݖW R(gdldS 8$'GM`; \%E£/̡JgKKa׏ 8d4.F#šqu{_rBR"Gu?0L\WJk(B bjF*E)D150fLj'ַ [iðR+?ۺWrd<:m-ɾ 3œ0>ʵBjiLXg/NfA<V"8=5;sw?k HW|M'ʋކt˃NCVKlZl^E"#غAyt? Y3wڰ@<ΆΔˈCdumѡS FuC;>5tN04j{&sNDURg8!i6鹁On8K~twtxCW*L -ISI`V="F"?U θjtH;қդ4EO>@"D~u:Բ @㦉oRJkC{ sr3jf,w71 |O%ᖟGi镃NI-+ڙ }TFO 'b9 v?@rva P)Mv]DM>_Or\sߟ$EIӣfsdk u6TRE A \ .jWut.ߦa]\#CÁXЦ_V>Uv'̱׸̤N0@>4%Yz*|  w#U/ 55  .2e`s n6ǻҝl ~uvXOyjf-97鎃_`nbX/V#Q%JMu}V.ٟ&k1atwTҨ&d5[Nlڶ5ҎyEZzR7zp]MҁAM4,{8X<Ѯ#1 AneYqn "/z,ϱR λ#O܋ Vzv/]qBE0id"ZRxxq̀BԊvQ!Ek!`*Yvngpaw!иsZ1+;R0Nj&3m/y|*"jbK=n:N0i/_=AN -Z(_{OD6@:O}KQ+VIp9fL!or)&E+#)/)jQD˼p$ } n I]x Mg Ue aϑI(IcSH0.Q;%Diā!^*x5`F؟Vy)yQd&y%ApΰO\MJMX97Cq /5~q}&>O^ |Kq7~ W[J]m=Ġ[;TFuN (0*ӬىbȮlskpCCfx<>T([+Q5;Vh6E3!zULbW|f QFa/a$aޣI=NJI¨Τ ^Oێ'.H Lk*l\z#ת,@Fv_=+m# D!/ov]bR.l#X>'>_궱ǮjYo`,`˸yXcrsA?Axa4th0a~Cz,D 6(z:EM zj)f}0#[8ph$?zh'0J8AEfc .mL ) `t3*uU"%~uKΈo8X6 tB[&rI3#ori6?G/ES).`X5G`,L.< k<&6_P铇tǺȫbJ^{%i-z>~'6_,ȉO/k1gk3ZEt^)}kۓ$Ourl "X!pzDfaB'@XdeJ⶚ф{2T^5Wȝovv;^Ȃ*S'韺U)yV3%MLgHڤ&HDl I$L“)"̿*{2P-7^'F𯉷L* UH$Feyzm75?1P(XTno^eזpOōu`n71} '@SKhxPscyVkxcq݊gA2;` Vr2N"R cD` M8lL/F4ژ̼,m򊅽KYFG$4k?mTOtqv<1ѝwœۆ#2- o^!k^Kֈ?P;29n]ʋGz+ M׾=&E-N7M1֓6q0gQ&L՟Rg֞zA")i3+t{@A7,WǢZQtkEIџ:MLvA!{aV\6ѹyڶRJxie h\0Cy%f(J3[̗Bn*K%'6JaKK K j#j{(J7WEʿ7·D&NaF=eqs+TFQq>:N>}%?ê'S}(>[Vo%'-E㝵HwMQ-WPζG`0  7 Ĵ۴ $t6a%J^*79P˜GDM=ckq:8%e&Jc^w;YAi(cU& \eF`9|vX7)>"ˤ)r FiMKܷXNV[\U 2tg)Nk !t{u5J7Rퟗcc/t2>İKX<l ôokFT9А\VX`Tsuz3*5S2mHpЦ2.vqzOgbIF˻df8_=A~*k¿6꣯sIƎH5Ya cz2D$yP>f魹!J$?ЭX L2|%U~N??I,͜iyj8l9>3eJ6A `^l]rf  Vӎ%;NJ?W!Eo:q`b放JazI`vYdݙKC=Zd~Aw>J fx6=u\-ʬ&SeW9\9Yh\zniP 2;d[ wQ,)_Wkym+.epO8fxQYDqWDC3=`ۙ!/MU m]M ym=1[9/w!rcHKtA}UR1 ApC܁bڢLo42`t\Syh78Ͱ~"P3i^FWc3d]l/!x|^D-u0YIr>jww\QђU|h efcO2BD,2U,P51uD16)\@מ_o}UJq2c|+ܢ_60y_ aN;`tOFWEmM珓Ic]seæL͇t"MO2ҌK҈P@Ü5>zfVAK~@<f{y{K>ҮQ<}sᒣytTUvu#m~{9z! .y ?Ƽl8,GMV}yސ2?I$~Е0raǑADoVtSxPl=LbJ,-cq ޝ%oӴ֪/p̛ѬVud#R`~)8'%؛#{MiMy!>(`%+l(S~`Nm2K݈ix$TlHbOBe{@g*\9iHf۰"$~Gdh/J! +F38i*%vI7ƨKc[֖ir3{,l/l1(z $\_cW3+fD㺠؞-|ɗ*~r_6֣0W07z'b@kpiغH7%"|ZG+ HL H2gvg|¥\/yHZOqWjڌ'BjBG߆^To s_JsԳs&6ePViDqx?f.mn1 E\W'op[j,ץY* MnEwDt7%~CӼOֲL#v!gEP u`(C+p˰,"].A4[$BWSyBOHVۇm Ǧ,w5 4E aѻtbZ ҅Ԋ'0υXkL_E[*3n=L=3?[SIV;o~s_ rW3T6PQuy2k. ϕ+rڧ鵕 BrY~QC02:2Q-:. x*Mm S(852c~M(*_Zٽ\2!19\p>}l=(##%+$nUT;H-xvDXjz`"e}֖W*y2)%YUw\NN<#Ց:%Cҍ/{@2{Gs Hedc)iV6OgM]t "3 u/&Rj#3pޠIgԱ@ Iy bJ\zHIxCHp~H([l=2&i9::OWˣFʈP3ƺ˜D@RbFmvcr;3'f3Q M5H0W^ſ Ne_`kܕŹYRM'|IHc]H,! +Jzȯg$ӶVZS-dX(yn@]p~]y[o+P`sMO,0MD25/H7kBVJ@,gNܓ^l]kpp^곙D2.XyN^ 'R@ \>DqK[1 ɥZkdRԴ'6-hAk՛&x]cuSKLNI-LG9dKυl4Ga<"̷lKaEpO*i ש{XÙĉslMq 3mE!Yf_N=,^ŭyN=EYlPV;w̓ETsm^N Ma-7ճAah;h<#, g>_YMBŨkNya,' Zؖ|NCw+N"a< w-LRV#oؤ<ߥ>)RЫfܯSD6g|Nʏd  sW$p)orXD%~P-6P.Ag-2ʹ$҂?A<()?@ D72264φٯ䲄Um:;Lj]VV*N&\ w?rw ҕ4AfDy^ ѐ#7X#şMFP]Z૏+ӕ|^0)5 %cM?+ʪ̰ |rQJ@|脑rx+83AD S7|e$X0| hLv⃧-cCGZjJB E.j Oh@ )-L6٪>6 ~:,-튗K sgY}P!4f1uoh$yu@V3'  ? }n_ |dOKnމ'Q?R4\Q E# "{x/b ײ k2O%`2Dβ]&BvU 7}MԂ\i:d&|qk.B _$6b'Xr`Urі;(1?xPB& OY%yhzN,0 c1WCw/%FwELX1QI@hQ 9i'sV\ 7`{?\KR 1f#{ S~A/Ix飫}]a\MIb_ fέ@\Ro?SZ.EjzJ65"A]>84}Xitg):/6ndyO S7-J)typuEJ 6FQo:2ˎDė`Ocld+f*x[n+`Aǖl9@yʽ4IP59U,=?6Or۝"R<*G Byz\0Is\FcN[i)r@A֜<=Iش~rg$y.a޴BJ+ŇgJպJrSb@[ 堓n"k"W~W*8-Im #N`C -LfUuV~z믜Ǟz42 i|E29QӸ .?cWtݼ YX>>xuKt͒K~=|i|>aQ:F qVG켹Go}ﶽtTjQX~| Dh0`ͱ?͟-1NaKb-pP7hlBqj)PmT9R1xU,S e䠍IVO "VD !o~B^Bb$7C%9Dgis -ǻ8!_K L[Ubᦟe 85(?ņXwp;Q5LEV T;460iHMqsL1$,ӲX3ddA@ x-I#Bї6=)!to!,~鶜IsEk$ x;HZ$FdY+"cNc /d+@}lz\SU5 bQT" ZICA\m?#yR}6a$z&E> B8Qwbalq;i- vW6`/xFW`Ш:pT4!O0U4" bixnŮW* w} Ag$oZ«[ !1m"b٦W9 1̅tk5QT;2 AiZ]U=]m2fֈ])E˵ !Y s!7用nx =~yty{}m|uIVq6O p#$`=yt @>q HШb$X<3 4&C}Dɕ<1a 47.vwl4Fϯ]_p2o3 v=^Ul2!Zq!H||j "\/2fas5܀@ W]\̂_egZѣs^!Aږ/+VH`H%@u-鍬^ghvhk8]j^ x'-bg=*zs}?W=16*F!^q)2nxJS3y4 [vM'/0:':#77sg='ۄ]ha_nZ1iX=y[$Ɇ${ ϸ ʨ<"E~s82ƫƯePF$՟)V2b.*Ѹ\y.tφBU$C#r88f=U[)iχ?eVc-(P>8g \^}UXoNȷ1ԥt0g` 3G(JvL^}!n~?T7FϏMt7}I_)}c؜(w 7+6tatb֨uaw.NҢ~@UK! A]UMjm~+$/ ĵ9|h/'>vV̓w>Hq;)gǷQ^E {6fM}JMwhA%;߸),W r2hZL[_/_a_Y4 fRuO'X霙Gy% 9$ /ek3OCus q]vN%js 3磉 1cxJ% ft~ho>߿'IvJN>o/yXvBkW1i)y`l{2P4&2r*+۴A}\TRpg}z A b=vŘG.&!$ 2%+Nebp"9"_> xArU苄t&%%xb[rhvlq#hKGx5Ov=BTI6NSyT%C~!OkA:뻶5dx&ceKL4mH$0iQKhQ8sdLFmJ>rΪP=~J%G<1.о36ϭ DHP&Ғ.+K֢>-5ganC-BL塥0s,0u4"ԯVn-->/@ܡ+Hq3Ä^QR4ii&p5C64 "$}O{nEd?q#?YVZ 0ȢݫU襾+iӃ*m6cs]*:Lu=DNwp:L|&yFѨ 6Z1܎D#zu5(I$w{Nehcl/ сA5ub;wPFXqXOĐ5œZ?n} r=AwUkfp0,DƎn,)4>ezb,ҚHW6.j^<@:915]@:ȝ~so,E~=GP٧fGHJ(3gV;Ie[O\wCzY`f}gV^A<2/rV/[zqD҄IJmBRձЇ`{ag Y7Hl.+gsiQaߺl 8n%,T\z^ٷ5V7[[HK9y2h/-1dq¤1q`]} X;%pw#`T}{!<);)e}@9'48 VTƣ Ai&<`&nkZݴ)슙1$\hNm29c*=Z=~~j;b8۝aEFR~fr++,uxR ,"woWAzkw]?9_r֕@/Rl9}ackyؒ%=1y,]LeRA)O.8XFFtdf-?zos眀lM>nxɛO"1 FMhkr7i˞kF9kpQ^ ~m|xu&^V'8{xR2I(]K^bqo`A{YM0S'5VmC,Xi8)=9⒩Tp#hWGUYyӔ#)2e/X/cBPYL*`'Kɱh9zp-pPVIn ֲ]Ĭk`1#]=Ad>@HǕs pKck N QiOUj{e KTH&dJϽz,y04 ѵtI>.(K-D&VADG.7I +COh2Y\r=++x>Q ;jh6'"H\U)qdS4@Z.f? "|051#-FCK$'@50`\V=pahڠZrpszb y^DRL Aeۥep//f8CAib]Ynq/G贯9^ {=ȽBUD;L!iR D$;`(:5qk'wF1h.$Fa3we+q}䢴 27\ dD`be 7^O0P4H# $47t]ĕJ+?ãmh! O s2a˿m ee ?jr`ҿHxTɬf yGXgHs')unEn?% ~?24wtӛV uuӴ(,P?P;i |E'S2Q0k:ш,uWշHY38~J3rn&RzYR|w^yׁ̺s, زG2Ifw L5 -1*Lp˳YbRQ3L̀vN4Yu]c°6R0mUh3X;5D9Ɲô[ZOZ}9KUY0B }ToiG}^g +o"c%ieO5JF>W0MШ;k5?.KW( q|./D+b$cZ%I4'UQZ6aqjы1df@-/\_[jW>9VrwP1+x>\@E*_<5Hp\i5i;H\d)7cjp`h:>2%KCtk-/wny/: X>s9E,Fx(N0^\S]\OR7ځ2o⹶iȋ0*Ogٚn?95Z ;젾 N kzDCVՖ&)ތF*r> stream xlcp..۶m۶mݱdŶm۶Wl۶WtOkjkjɉ%]ռ LKRSd|hk,C oLAnɱ - +t;OXu)`*Z< tO;xU䐲G54mG`?1* ya,n$ҫ ҌֲvqSd:$,)p#i \YxM}"pk,,Dg;~;*!gF,g xuPS4'xEfnBL7D1>8AuzJLy1ɴZt\]NO?P[x#T9E}.cG”a-S*9PU!GED .T_T7|Oba,rWtĒ+toP,@ C9~h7d-i^ʲ0M:W* ت܂=9Эq;`!)gbI!ΘD9Bb#C{EI R.GY|J؎{ڵN[]R: O3J?=sMgP-yZTSOjs@"7+Ig,Gy80}kOleqR Ŋׁk5-4kPbCYi`g߷6D^5qOB/'3ou@͹d| 7.nB]h)jF @ P'5{W9c\_it0bET?.DMߡdh{Kx<pnz7T %^j&>3)`Fێ/c;;-))GHgk!aʔvsT/UsEreًxH 'rdg~4>>YuR`5W#pen'`T'2rhOY~\:Zj/5,3Z*~$ fݎClh 3YS;h`J&'!m+dΩ hMgQ-|?%q > jbЛ#_n@,裯$#97 D05<[O/\e.I2C@9#B%\j%,i 2: vS2{ND!*4ؼ6JK o ^^VyD'+&Θښr6$\a/35=TsX;1 k $|LY{wC%m5QsZ֋Dz ʭ7xNB`_é2۶+g^ ~(uJWcuY$x`ZcLc9{`+ xJ9TQsGu('ͼEp-9}q,h 5-yw%/)QĐeHO\x4^zAZ4M1>7pABˆx0.V06(d _8+r? \*ܮQ<8LICWjPu5"(^Lza~blڣNj(_ױˇ_­X,U(K}ʻ]y$DUt ߁ A u^;.(Ynp߬(a\aK';(x$SP͍Afnxٸ,tHz]ST\^CFg$!;{䊂CK.r1KaX Wokew4.m8 *,dYCY\ap3^ܶ|"0~)!GXvS_z>;Cul-E:8eޣrp'bDǚX4JV>qY`/>Uf^\f_'AbVJ%o2&*Oz5]x wM֕Jc nC.UzfPBC"SvpvH2@ u7 /&lZ#(v|q%IDZ'5xj%=L$K@2k%RTlQd+裵`0K- *ʘĽ0֦)Jy$^K6U~\/9r>)<>gh[7Vg!aѾ?QӢՑlܐQdiyXb!L. [哻;m-Ku ROs ķ:fÎ GP Ur#x g$%ERf}TȂ Xsjʭc%ၐz6GC48VE.`m&<=nur>oE||?O+-{?p&l6nlrz 3 *:8Z .oR]YZ%\0DLI=_n`r|*"0ˠen~y쫆3ʦјp.O|.Sܘz< Zš*+:BWڧMЁEQN~Pw C--~xj\8ȕ(QYh5@*c'%5S^jd?@|fdD/.J IdwakUPq)ĢW.RD]N ~%&Jݸ!8e"ɾj\,H=)c #]=0kHL&n4J(+JG#w|oeQ3ݟ wQպ0g-)z@upx[2+Ѣs0@RK-RTQb{rB?t[Nm 8v"; :CH$>"~cޢU#$сxZrΓY{jMQO59["P7lHwE|Fn?kh)@Xs=5gڑ aTgzu [O1SnRgS#!jǻ3M)'@dNΗe_#,|/l[#^j0eu< ;^YWfZ~}R4205Hdm{)57K,,҃.P/ݳ8h6X#Y`y VJL{slj273 \Sw420h#:)Hu aM߈xC^^ ?͎bMz}&f@Lna9L &f@L ԕuh`V%IO7\ar7FMV.M 9Kf x6j{YUOMa7帹Wl\^(טFPӦr]G Kd7:خ0_Ν[S.z :)&=|ŊAʆ;حwj\&Uq+YhiqT*B+EWCnw(s𘪴h//\vZW,19rKע|SXv^i'^k[!<6Yz0Һ'=`'Da|yly‘rbMHN$ dʠޕ{fa-E.rW&ʥ1Fй\Ho^Τm KJzɲ VLSx?\<V>LVO ИLC2`90PTTZ+^g[OySuE  )mK1t0aNROfFB`w!3U=ƞs0Wծ C›S5WV f,"%p5F%p$+X\=Yj)+ZCfY>ٱ[wkUz|/zJ(m;_^R+wSt錚wLDv)kj4eg+o+'. ?t"eueݵӺIPH3h8/6DsG< ꫡB" ʞh-a W c*w@ȤɬiWQuT"dʉf3=ke+b@Tw}ZGX4dD-ʧGQ7rg*WfV3NzRvk+[,ǣE_ vB[W( H8Ah0Vv1˯7;j$*0MRE!mW+"t f/$5.Tb E ɮc#wnpy!i+ԼSeK5 c׽R}h0AJѳGŜ:1TRI Ԑno0yOsDIS7X^G{B環$?< acⳕ6y;bB#]h:л/)n-4{:D2J֦X(eK"sgko P|z=4$Hhy3QբKR.Ã;F\#:N}_π#Tj6ȾI~=^J_{h#^;+ݡLB*LӤŴ??] xu<&Z'봩t &ʶI.l4niT|(5M 4y ɤ)~nB;8Ŝ]O@&I+DNN>W*_ t}Hi V(5C4#z+IP뜊\T2}-))"$9>@v6 jks)?Ⱦ+ʗ5kh; ή@.HUMP9<%[bJ 5&7rqAefea=IA;GׇZ"XV;$k$6zk|'j+I$2,"\ U'Yey'vCXYUlI0Qg.Z_Mܹw`=!35~=p1&rq*Bˈ- f[b/EnZf+/wY: VJЦ!)v ϡ)s¦v7Owhj  چ:d0㴕 PvWZ1/↲'ClFU3ZO̒ޝ пP64}nĹ"c͙N#mٵgh+S%Պ24_ӚzEp|rtmHX~|lS{ |8F-MF?rX>.3Y 3uo8l̎!pev'b(Vpf>݌" TIj-_"BZyQ$m%MZK\)wA\yWtW;V~iE4Ί-S4;\G^qN th`A IDy.n#=̌(i_t>쿬=2x#:ԃV17HD GMh'8' ;>cczBܐԇ}1ö]X^r lju,&xnY$ەfSB t0 &]}0J~(D\)ɬ5zL-k63AL%`,Ui ,?`N3IhC5i&3%|g~Vc3YpJb}+ yI ̄F^_s7gÛ1JoqP;݅iZ o/BM:~a.8yBO|uz6}=C8I/E*"eP{6IʗdĢM*wЭxN$/Y%z|慯re0ss#&W R;>uZm\^`WYL]pj&D`ͯAi45Ӧ@z_@4,ֺkh|@ցyehnoUK[aQ1vLt*}P4`m,iYtݭ >;QZot|4o`?:Ǭ (Siu@޴ffTe7fB4w6Aj&0(@ۏ((x#ffҩR9HpIFe7P#D8dC=9Cϴ-?]4<$K]J~@ 5bQWu鄆cp=S2֊,01젘!V wYo7xT釦+Y띒#oSg- H7^IFuF-|P]-yϿSw+؎1'Fr)AG%LL; \z|a9<ݛQv/sHSP8>Ǫ{ʿ$KakЭDo)[7:_YjlTE,,tAB1eA>HQz&k5Xj(M[ufC Ta\{Ŝ4az(ggh"gx~_>,^Iɜ(p m֘kMȏ *Q74?/_$'DIMdη("G(l`%]m.!XXxCU;aQ}A\t dcA'C;Blw6NfK5!W9j/}h*=9`e+-؄O? ;Nd]ԡgD.ښ; X 4el BL67EHnK¨YUvEl)ЮDe*OX˦˂`NK'Hn;0%4Ul?o@}\4?3 sD*9y-Mfqdx._f8d,B7AAG [x#Tlt)$[@ȅZym(*e,Q_u'("穙JDIt@6m &7b}`@-;|L/9vq1WK`+Iӿz?U!WhsE^W(8h[Hp'Kֱ{4vTK JUϸ!7I5raAub>$ (]'l ՘eLC%"vШ)/( $:ǰ@ BgC:P1>8{JY) !W=Q'xR&MٴP~"ڴأ+ D(7u%)n,<<_R␠Le! p^?&b cr;iDy޸,X܂VG0<"da A`P/?|\ fih:+{z?ˮ9F- S mL8f1(f/([E΅pxVS r'hfg?8֕pЦ#"gn"D%zP־9K-V3jJK<Nz΅<{;{y9jWHA8b.ĜHha'V7Ϻ)"asQY Cep ;'uҡiCiRnW~đY呭j%q~OZt{OI<AcQgaCMquEz# Tz & e7X>z[AN`*XJ)|N6坼˖aiaDn9$VgYzV|+F5c>6-CQNCu,|۾;Qaʉ0jb.R.+nʽ\D9 AC2hd>}C8(# ՌHүK_Wf3E?VAͰK49+/C.u}>Q\Na!E+[9XКCHkZ|#9 t~;y2SU%MZRdIKEI>ÿ~,5zv\F$S{V%@)74`HXnO%T/U3DxBg%Z[ I$@)S55Ѫg,d 2yhs6 /X CIrw Ds\~{Z.3?1dӻ'4+I h< %8j%[qA왙MnJEZ3qrw`,0LK`Fx` ͗ΣQrR#U^?W[l 'mBF.OY qx=Ϧ$J ;wD5VJa^Żz}L7$뻹>S. ΐs@8^ }^Wmzq :?@7J*qy o>2}Dmqnmh;Lߋ21[!}@8?&:W~@D}oZ3 U-$Nmr1!2}"rMd[Pp+b ) 5)~o6.`tL$_KIY%XO %@_Z8@T^'Ӕ-lq -N|sCM5OP~?F>~>|WmB/޴?Rǽʣio%Lkˏj(G롸 2q D뚉-K!ɠv ^n9-1m0L:,eHW(F+E{mO^u{#K$vwW4d B@k}:[mo`H*4y's N߯>S_Om ً圿%2Fw̄mŵ]gR1y_Ìi'txck0EѾNy҈TM}}mÄLGfe~ۿH8 K1=ٜO@_)(X0k7z{mv'~J:7 Sv{ 2UH|!pϽCFe@; j_k$^YGO|DҾӈJ?/Mu_C(XaDRWV3K]懲 և6reOŸf8c"d\R#}akF fq |ʨxĦQ* j$]7A*ެ)ό*ni̅<&w U^}0вWW`ddo5|ж$`)3Xb v* Kl @lR&bӀ)̩p-ic/6f/̒)g2 on%>RbC%/O(Xa|d *_o뗾Gj*iw:,>5wзpkO~-UkH\plV u)wLhq{M2DLy9';0` 7mM/{*Z`T{~z(p4AIEn-[ :AS۽\y@z%V0#ɅvD rbg8VS5zMӝ-A憠b#>f\m+SD]X9qoبHmMN>d8jT@~$t"Ya[pűII+$VvC Rq! >ϐrk3*zX65m%Mg^>aHCtG]S, jă"Z윚sF Et{"IbE) +5V0rєL=5nw_]7\F{ .زr,pOfy{cOgaG([}V8LfWU C@MK0(Y1eo ux2(~ŠUs  Hak[%B|r5MX$@v*˩p&,(@ %%nLH k)p#*Ba, 6^#[G=Y09$ie`'HOm>",N46fOˏW:YhQoi`ǘĒЛx,sHuNbˤM6@jvD|rӄbjaKf-O'>?) WY"x!vMѓ\0};9$-X_FZ+i2|M.p{6T Xvh1'JQu)E#dӟf-О-ЌbJ!XTؽs_\v:P)"R,gPLllvQsyfo?7hІ{rJ^(q?@^UfD@؋ITl}U'LJ {xܰ̈&T;Zi.9yV&7"M .B!:֙{rh|e=0-UMЅg>k?/ž<)$Υ7L'qE\"LI>IP9ֱm( O4mHR(@iuHt#Zu  h }.EF;^nMI&*  T,vb _eC>w!Xm ^TbZ@݄SJ9[47J05xYff¦ r\ gPW{n c*%fZў(wUBEjsu;(E \ͼs]ȋF'z6?XDćc8%`kZ`1-{LeM IxE,q j"PL-i H QfQw؆_D!1**2L4U?[}ɥqW]vEH|g5Fpqq` #\*3hk^;ÄNj%qLim5%H}I{dI^ݤ(wKDN-cKΡy˪B ϓPRRb#(;6f6))=eE4eh'r`KSڍ*)F"fCiV5߭M*C/[Xf4q c oHM%uz9!&\,3I7%pZ"z-aDm";$q4SVXS̓zFeƻY7HNɂs3vzV6g.oգ㤓q Mrax HO-_M2Ou°upEYxQwG}]#^FAe)&IH+(&ZEB7o17 V6<)[M݌{z2Hi ˪tvkx=}[նy5Is'c9># W;I$֏֒|{'8Мe8?k_+X 0N|iЇ$Ȳeiw @@FǂN͖2[q`(lٓPƗ&;B0uR'ڼz!+{_CI]Ga+, 0K!!s`L`vhMdۈ\DA,m6FB oh堐`nt;Lo 2-zpxfÜxdbӓ+ԐQy߸;}A?Ϡ(ra":#qAxNג$yLfa~:Cɚu%"[{t/mRpA0gۏ8mƊ%eRR Ɩ/A-Aԁ3K+1ˢtHuΏ+m%cx0VҤޘ"dʖnu*i?l"Yls6ถ ŧd" o ʄY*!$O sMw0.%n.16/ ~0kupQqmq'aFRjY_S62vͧwÊ t, MDLTeёNSv>h ` "3{57{$v۰oFσύ3NOӼِ}3˅jL"rdR2 $aS,)5{L &Be!f3:I۹O -y-6QO5W~|&t5GT JI #F) U` $N[G@oT;v^\e/@ZҜ7Jn|sTjHMuK6Q2(q&X+Jc^p]l0X=|Ng&D endstream endobj 116 0 obj << /Length1 725 /Length2 19267 /Length3 0 /Length 19821 /Filter /FlateDecode >> stream xlspf]=ۜmĶض&ضm۶yQW_Ww^v:!'sQt2330T$TU9YL pN@#K{;1# @h P:Y,LLpQ{O'Ks  A@&*O p,mQE-iyI@ht2(X,Mv@j?_M 13'{[OqUa yU(( 'O3?@uGp,'`ji0[1K7i;3{`SWC~4v7Dm\]N@';wwT#[Kʐv1G a;cd,a4UtGo'W` '?4gWMlH;Ij=J /mʌEh*Xxn59E~YAvރQ;q% Ay&nt 5ej{YsGHNAwkn{ݍ#n1S}pA!(ވn;o {תœYkdc)J\_n|)E+*0 ǬRfH`KȜIט-HB Gq7q2ʃ!1zAy!#~\,8x؏E+GSEv{Ml9N;a呕*>M O? gff=[al=-q+hf1S[]TͰN.񕇋)}f [̷Ld Uq6 R5Q)s(ujʳa*>|<Bî5O Oo>nOa7>np]4~[t99]{fA).[EO n^(<)GJQn)Xѯ>nM5_p03=ϗzC@.cܤbi6䯩^U5#\^4u#c) Cd1F (yAf1M77L6[obOX۴TQ%,& {3 ٤D X6i0PңC+m&R*,n[i[ބ`&!cwYPY *'BŊWh#N?[6c戴C8QJχMh!&}( ɘNaAsO91NC:==~d0ᛖ\ZV80vɴFs1+9/8nR PmsϭىxÕ9Wqfuߜ dӉ̞w%W ~_o^ $: 3vG+tڪcf7S"8m"G&ꇪy;X8G}ݫV- e5jT-(D6SѦnhjZ7B-+=cz8et;i{&xwN\*gќs6a3 ?s%)[(4K+V^Ba =g5U%.i+us2YYp7^`YSs _-r."T3VT{Ge?C$."Id-rAaCW5!k{rKmH~T$İ%+A.W1·`rU~3ZH+(ePGH7Պћ=UbMctrE(\_s#Ix4ٹ.efTEMYÓ?dK=%ckv<Ƣƈ rЀA&qϴXx^vX 3Ce=_m bwk\M-Fj"4g"zz852 H4ik-i&ZnַoTe3ʁ4GbXfn$@5Ű$3!<+"PZn:o[ɳO=#~I9':2a xֈ';Rз+XU2 3@.+4wK\yzQUO[ FIC-8 S@ÌFn+7hdNUoYU^Ka僾*S@;6]~F9<5ͬEZO~jݩq䆉Zu)0%^1ϫ:1鏠EG9jJ-adHPQҢK 1bƸɧp Y|& d?z 9#d9nIJҗ }.೚n$Pʌ{S(]}Md"7k53hHCk:g4@,ܽr},">Jq'= \s.&h~Y]eC9lETK~}UCTeN1-X%D^{GQ. ZK_.#W#DŽs*#"{o7*x% kNpf6N, M+MEŰ9g0n#txҌF2(V!YvmK` s^-ģ-mw ~ԹM{Ћ/t8?ɃJe ͒/9xpF2SFv&xI5UeI(LĨl˻朸LȜZ<6Ds~1>:U0BI4<_^b-nl d2[kS76}ڭ1RQ> ̱F!?Yc,$NefE0gV=(̶*G~D a7$^HA89Im7RF+b>RYz8ZhCec~ 6Eypy/c_44|b9QY[* ؀ԼEdQK,˥ Й&7 {^ e'ږ ւg_X?"dυD1ӌcGHŷ[C"v)j>IWM}H^Q. bKzi!ZD>$q@/cg6a- m@s8 kO__:(C8= H-@qJ89kvu/ e*N7ދ'~yFxmVjVSq9{J~"J)2p7 )'aD%Wi:r@]h~/@x`xC5Ztر[޹҃S|2=nۤ bd8\%fsl@ԦiLzKYfHNZGgGi{iy9k260uXcT DcTn?c<ެݬyb@?)qU2ɕjųGZVtZy@QbKWKO ZZ՘vsHyLsϕkz4j8~1NPvfּ[`dOAaYh9(C4s@xwj cVOh6 l%H }4(f8 Z3IaJTZ~'C|:Ɇ&G)M_H?Ft3-ev u9 0Ab&+>2mB^R~qpX`C>;dX$\[1ʹ Tʼnb*7|44z&k/df#ahA[vC `񁾽o/o X>K{IKyS{2c$J@l~Gh:Dvfʈv` 6+ Q1+e3Y6vOUdDHU(A7b(yDߗY):މ.fw "] o)854?Q>[{;V XdVxm \7X9b?f~O$J]n(t5'UHg-B"o'˖$ƒ2o8gY'+hv$€ҫ*\ z`BM_hM?dK%`IV]C}, -CrTiܲ-8W aw?BEdQ3(dυ==%`n{~Kfb^d3FVYduHލۃ|kt'* ֲ?؆jrzluZ5TYUٕȨ'$&uIk? W^-'0 )ǘ gIHbZV 0J'8jwn33݈#O\W~AGuxLдtЉTMW4=+H<$_zpgtUYR̽Osgv TB+rRSލȃ0)L׉궪MZw0G~5Bj m)X=x~Knm :WՖFA^AANJE\F$ 6Vt|* $':B2x2gi6W7ꅰd0^gmN$O:&^>Mrа&Vy\4q jOʋ'},S+)C^Y~aanq2B-q~(rNĂUgZAI7NRz5aWcɢ9C] $#981aբΩХ ŝSDGO+ 't{]!b-j׬ɩJS%rkjĉӍj8#ZB$ *0O99,2K09Lᎁz^1N7DgzջxYy2.ze5ئ-,NHRܽOvEk~ZQۄ[{lK^,]G<ƒ1K6U4h;]90FOu?Xp.W;0yѸE_r3bN|r'5 F &?C) ;'["f߱y G#[Fc9LTٵׅ`Yu>t {]>ih%Iй[yF˳\y?#!xD'xW}Ak8KHDL8I Dzsh(LOY9FCy^\OݭAVS#u L h=a~*'?0Ocʨ~$zӍ[mVX9m .h#bѢnFטjL+nwkۦ a$ĝA{QkY Y7s,N:sZB~8TUOqoC,On ?]:oLҶ$꾘6 dvh[(}~bJ)%FY}zx^3bTאm$8vGL|`[pX^L/A{˯5"8u^IJabk8 V~(-_ -+#T my}pqԹQl 3/[S&bD5Hrlo 8)6S]TEI66g%pR&&nJ5`fY݅EsXò ' c_(\}4exNBpgpfm z=G@6;.L9<ݕmbHF/@oԣ̎䲜@~2u}K|Q/S-E~xbPHgZw?wgi4i;s: ^ew .j"Eg5K\H_Z#c7\PS3n,Q#}r܊uv,iU u%炦,9ksho ONXcl0_(D<y3AD$%(0 "L/xs8NM9Nʓ !Jga#F}B%d &|tR=m*f<6Z,oϗf ï8k"'mV"hN|y(AN2)=U`j0r?r2}}$|O`4Dn:5 l<ʃ\ bg{&Sű2G[swZ8vm\ӻ;zb:R8(@3Od}!'v-I QB,~]!-ڈQ~`kO zK~SkhdoZ]!_~IL,դk0/hiFYx;:8~G=X geA%:my/ȜӅ]DaOP2I&!]Jl^NQq$ӬJJ8Dk~cnҐ:^0N[4aX8m2Qqʊxcs` ]g`{vY|shאhp;m62{uPv mh}amxQv-z6;׈61hf%޴ZDSL-"QǦc",GmE;n8.v ErslRVR΀ŝiaa=y.c+,A).y5ڜXb%r2ߋ`6w_U<6[>ıid8& v2F$*]e}h+PJY&p5ܒUeɃ61'$ga_+m &JgֽdASi ;S,T}!^e2_`o .~' ;xˆ aK f(;7O(tErzMj#93&47,,kϷXwa~Oeʹ`a V{XįFP6HwG`,pV;YGrAB Pb3('R.&'R-#Q>:mqYXփMh2g<%(.Wg/l ^-4j |54QIK:]J/F=;6 +e]N$-`"q|p)=.q4/r%^hHUILvv.՟N2V[E75+5X %9Hfʋ@^%Htz3g? XA}`@y~&hJpQ1Hxg:K(-=cxΡ3NGvtgq\/Ks4G>N)c7!kex6'[C C3Y: /WM3+)Dk l9tPb3FeW a$jŋ4abi|^}EN>[ ~vh*WL%ӌJB!S`ɜ=̩F4Ens9[y )[+Ir- eVtG~fL^$ھW3BVf2xӿ}}0WՌbƛ#!OU>}7"J#:[kY 4{nD|xws'dbFlS ~n3;v-gq hfXnis|m>fE3r5ڗ~g?8n)SKs=1Cm%b:L\hċbP0bxoO"xۜ$ցi dR]oB'T~+ Rw aɌ(^h]= -L+Ǣ55lh.U} ¿}aʄR~{Jaˢ|,jg)-zaK`9,IT䎩g >*.ԩ w1("H(D`Үc@PvOKV0=z*:;˫.E8*Mﭸg ?syr?1t#iG;]1h*(Kh=qW  [W L;c^Mal>ֲqW֗C|Jɒ|2* gtV럎= D\7M&u >&02mMs+d\r]ڠ1neHJGOAO${07 Ύ0#G~+ $' /5 8<>{;mTB܁GeڝdM#$nRD2_6 ڨYb0L`mx9YCG(;u_mk}нyy zC7ZЗc{?t\LnRbnMIj2_7#/b.: :v6Wa\EV65_ @r<`: {S&kWvWm4Utnn /73'atb1Z(f-+@U%1ifi (J)x(Uthc[ ,۰_k!n#}nK}PHËg9/|/^ hirKa65yCJ dˉ3 p~L̉a^vc7"BQc,z+8B%}حns eJ';$OfTiȴ)\MyT}~`hP0Nrf3 :ޱih>-Ip4}3"Y챇c;PwAR% "Ha7ؚG%[gٔ#z%VXb1O]bWjF|/5=|tN\T% aYi\qUr>G[ojVcd˪OfD{{侊BG<81#N/\i ԣt?Z <"e?K/ycdz@+ޥҘ $7nQ{h m'Oe72T}ęG2(T˞-r:d $\0!{0T[tؠ0aO+?8,6ʇ̀M qܟ~='NJK#r/B=U&.[yt]@E>),<VO ̬ssxԯF =%D}MLh,ʞ*+RLHJg>'m-XA2>o1hU o0^ i> dLJF}#G/luyrtk۟U"謘 % (yv4}M* [ܸf;\Q&^1-O.]X&ޥސ@l۫F3[-9@,XeuƝ7X%R )&,$'b#D;@`C-8s>7Wy'_)/E+.Wޏ,k0]&7.|ތ'oG[~45<$p|7oE wхBhѪnfw{f1:h#.Ҏ}~hD)ec/.6N"o P`؇7[ǁ=eKo&fQZ`E~ܱLjTx @-H`k4Ri`*?Ǧ=QI2* 'vE eĽy6#<_.]o[1ԵVBJr?'[>y2"vzq ؇J񹣶JMa&FL,5%HDecѕHn(N ǥ_N-gm{d۽߷&]8(~۹ Nq>lC>9*Hn?/zhT5z1{!Ȼrdt :k)"k$X-hUZ<ɻ}J8\XDx(iy[[Z8 s !]I z : t-&/")nx0_ קRj] xӯ ԦwfxtsU1* ;5UQ[<߫sOWD|0{%g}+!\KxD%-3,5L:{aMT8:uǼ/-7-,diuކIbK})z0ʀ"NBS2J8b[Y!N)K:&LȢw] c[s ;c=F5B6(`խ'pu]GrfkdHDc%;%ho+c_Gt{ {LY$&0?TdjA'oxMW/o6;k[x^'>KdѝbzYhQGjQ{??kX>?9aEcAO洍Í8 ?n MB`65U"f<頄NQUN/~UŠUd> stream xlspe-'رm۶:N:m'cvr9=;U_͚sԪUs=R W /GKVFV&> 7Rr0uh[Z-l6VxJ8hm 1Wegj:@v w @]ōMEꖖWK +$ VH[:X:T%- 09X ӿ8[:dPRVH3kL, 2,wpucDz3e?Yg[L +?:X\[9o@.!-j7;;%S{K8tv"[jj-!`},]*@vuvwXghitr8T-@v^wZ~ZҒ 5@>M-WZo/U@O> H _Ӈxx<,~_9Ȝ?{ZsXdL94̔&+d>pxV?h:Lp|K4p|"4p ڿzU5.J \ *5}u%Hn#3ZΩDM5Ƥu34!5z'.}MK3m+J-4N-u} WspO^lb c3By⥛:Kpߪh\0˧UҀFV,j.Rz( _`k9}>=% =Ov`'Tqn=(+P:4:aP~MrJۖ_Mj.`<2t =C )5OqwmqmB.: j<^;L`/s{hӛ4Tv6oNܚd``"XH">w#X&,/&eZlW5i^FQ 1W3"y%Du1s_:pabvexJO }0voF!zI{ճƎ:K/%"mxFN\j: FˍhW 8)NISEC$_oʮz <+R( FSIc\Dq`UQ5vIЮ6pxLdbP"t?k\GL Kr"@;Mm # [K(%TZ׌K_ 7_"(r9QAaOGPNOLK?Go]  QD>栫6H6QʻZK]ٲ$79Qu0'|2׍GTy& &¯h!6u%@9%B !>D8ED$@r+&ɵ5f~PPp!z# #:?x?.pd-H ~"l3Cb)#-vح 4/2<2< w2zkƊ9 ّQ7IH 2EѰ.p#4Q!ü uݙJVX Txr ]|yvr9+ Ј>~xM6cUcMz*a(3>Yu@з*(Y~S{MVgjC~XlƊ=F qo\DN`Bm.NVl:Ⱥ'_ ,9VќV/W-{Ey%st`; :{D$eՆ]:Heq gg78HhdS;P*laf'?6%27lyLuaU:vqDG £릂:v8s.%T:"ӓ.t܅H^YFs[W$'rovIU nIYN&2 wE!Լ]lBXpQh`8JSDߦe:6b(щ+v"evTt]4^1`xhTQ>DgN;ٗT?\w+'Tq]R'M FrwvJbM^ ,{ \ar HQv# R'R$˒X>I: Az0:r>9X.oZ9Fga s _RP qb"5Ja(K,0SHuwe딁~"kZy@X*pD*_3ʣPi -syLW'nȘ7}9{3ɸ __ 9D|`McZ5.w`s $3C^'":FwGSo2?)Gt`{BLfc4T'8Ԩ]'fgjfC ӷҨvObS0C9s*7Vݴ l p`H<j_N>GI+X` R`{,7/#3MŊBi0w1TPaw,c\ ij[xHu3@nt#G|^A!d@zJ=RP zJex`iF# q[$0T#N{)Ɔ%b㜜#Dm:X R~zreGۺ hxtid͎@pA7AGY +m!nrhqkd>wJ&q)rz KErI}iup^렇#SnM :Ϲ FMFl9$QqĠ0i" PZ 50C^)bKְ'~^z駋ˀKt.5f 5 [e l'>'=MH捵@%O\-+An8! )TT}mY\ WH<[P_J\S 1fNK>؀G!#.V1^Tmjy9RHQ(`1S#;anԈ6Ңc1Bv揫4)Ьv3E,coԩଦƦ@aOn%r~Ovhտ{ZGqUH:xa_Fh_gA Gȋ<zQO/!}b!bxF(VZ_ᏺf pLzbC&I&]1/}GJ.s1=ׄwػ{4rkȾu.#4TG~PWo%b%P'Zxv'?nf}"b e3(M L\T:T8 a|gNv;&oct]#K6ƽ ԕ8,$[K3N."d RX?xڊշVԥm^o^G#}W0JW|UUqU,.6y-fP9F6.ŕ\-ɦ![x|F6+u7B ?JߨKkk]kY:r)J攈0[MYyv&CVDV!`RsE Hzo9- 7(ڴ]DK8c&6=s& WljЙ񱪻at9PZMј,>E_Ȗ0p*n{N0YMF'ϫ̅&0ɎvC:17X{#澃ƭ`>\O f؁,G0R "F(|'ő՝! k`3DV=3&|)@ki0)|ygiJ@ZBxQ$KQ*4`M'в^P&̘S )*T!U \-<PNi( jKa,`܀!8O 3~g#{QDI@Z0GoM{wv$#HXWdz(ThxKMw Y|?uȰU^JTAm5toșZU+R~}G)e#ٱؽHbWoF5qP9XpGEAʤUC"81Wx(m)thB-KE|;4ĞV!ѨFo'=5 WX \k@}F!a/}U4@<Ԥ93cQD C#h%R 7䦔S>hġyxNpD 1+فah }&d!'&m[K^&43Y }p 25h^ B;EI31^J4WVg)]Zѣgb{$ osq JRP=;]Iruz𧹼}\Qp(v_eyh~d!P/-ƑEKRYen0p!o2'vw9չIoWJ=};oȢ@Cgg;&gm ^#]-{E%8VXjE\n YgohL(%QU\a>`VU F|sP̑AT#䯡R1\fԀ8p->nU cKyFY>l^lU(9п!3? C ^>q-C4yJ1GoZ%$짽PB L|/~TonǷF5hi{)ˠeBTɉ@3T!QgyVΘAi.&ck?ȷHhi ~ ]fZNM=M CXT0⌿'L8QZuJVcFt))is>[L(Bۍy(Xkl;?,M,o"zSBl2e`jƺ,1q%5RX_ ~4 9t9=$B2~3x~dv(yrp<՛iT}ﭼ􉛬4[wɡKspxe4qKxzG3CN|BǺtrRUݞ^'ݎϕPr ZU75[^eTN :E]qs{5Va9 )J teq"4AI`k|1͕{с='e(?8ivHmQAL&~1eUPO46@yl4LoiV=}hj 8Ԍ+/d@0y+$/G=)i睔/_-i]\H2vӓ@)#QVRUl|q^eĩ45V[\ XքjWOcJHN[Cz0w3$ɼ9w3ze~l 9΂ΎM[ =RlKyz튍ėt8H3!Ai̋AvԳQ[ZvGR!n BRܱ!3@r!n |EQ D u$,FW6Z&YhB{5a8F^giJe,'dig!iyu4^PvP#<ۺ4GW6nvP ]C9٤ȓV2o#. ,"QFjaq .+ 8DQȟjN߹%.uqQGA<ܘzXEkږ+(t2N'@"fVdF>זSЇJ}4nXg5΢ǭ8Ҧ_(8X'+aAL IwZ?oF1`)g%ӹ{t $j;x=goiCƊs)b T28hǥ՞t '#j1$I>[E|,ԿϨc$HM}S.p Tb>4<|\ML!xDؒ`sby6ӺшV ~aN{}հ-Q+ER4'Oē|lG;@u]Y S>::f1IgwT͘-$9 rUùy2lT#Ϸa ٶ[`qcxRkQTo1c! ЉdRdr@ňyx|.JuR]!!.+o}'ԉqsdF<&ǃMslS<Ļp{B#)!miktdѫ6';oC4/WGN%̤5 `Yp. ΋`Z|7pk=UӘk)tȿ>)pt {I\m\%9N;ͣA-@; jIoIq3rlb"2bz:QxBߧL(XE굅-#I,jIU3/WW.L͛-Y&ำQ-bQ>Y zĐz&cWZNz 20XO 00XΨn1Y/eUYK=-*=H^czlV790$Z.Ks?bdFo3 #zb\L^3Q9n\eP]L}Z??fD:5&eZwlGeYlN͇҇ h? ԄS,n&ۅ;VaB21#lrCCaѰM;j7-x{GJbt@cI9♵. LnB*mEm|~aIaIA?iڦZ}e !WWC~J~g~O ;8 |٭M(O[41dV A%-z%NK#hRx:Cq5S#P\5vN#*Kj_cJsU}7j4.璶2+#2z]]d!"Ul뎺!|-dr@g ^.N`P]Mgbi$L?xmt7Y3tV0N ch2w3JI1#P Lh==GdjBTzZT ԍW>YMd(|nUtty=[倉_!R \+pך)J2wr^O7A%loD' Hb1M[s%\ <4BGK#|RnÓ9!)_i;:a)!(~z͏ tO+wԔx*_ej'A6 z1N) կ)`N$i娑3Poh$e_?J"([^oHSʄ7 *=mT5l9 ѕC9kRgi`Huf 3P!d<2"I{ŇD䃷&ֹ}Jsc :o끏^G^Z3U?f%SA[h#k"bzZGO./jT?&&Č|N& #WXHqG2]@cm|1=9Hpmf |R|[ȄGfϥ!Hӟ*TH [&btɩ4gQzygₓϲz /B5_S Zh'p"k7`}vHTWc7=ўƱ;`zzPY5{I-.Cbi$YaYmN IƩ]{?gfWjƄf <'y> uԭCSN,pGmEx5 )M"&Klݬޭ }51&gH7ge7L￑&i}i栻j͑3Rg(;s)U:HVĕ:Z#G( 'M+[d[1Nw8`$^tqP!vV[:{ѯ>>\1δOCH 񇑤cz1Z5Ɵ6w_BXJ~1; s)a;=^N>7P b 2qL$"r̄oO ]gK5zO }Zy}xxi7^LwU;+o Gl(3f[,7ڞRj7x_ * ){;xr [0d]n*}\)})t༏1J)ľ=Es |fg| <2k)k6D?'e< nݜ86qm=Bu>2=FOoWAKhHZ  O06s9r3rMT<9=C>+pa2k UD֍_ҭrQXP ! rP͹F>S7 1ŵe+}ArO2#nߛX*(4$kiSyG:_42>ed_z%>[c8Ynu`s0RfVVa=Iv}ڶoUPٕ( A *`S> *=֙.l$4]oBEܽpئd4eHC@A_[-i=%kεf0ZI`רJQu6%ͬ4Iq|s<q/`x6IQbv tI^C`WA[(n%] Qڥq {X&i9f7P{eIwz8&/2ex)X9f| }_BlH 残,˸ie@J[&vE\R>V7nn A$ c?g!kȵ߇*Zha,:oi8W@'# 7{#*  xF#dU1Zk|*6y٘Jz~PN`"/M4J;JJ1>trm.匾)߅=` )|lRrT>vT"ǷU.]n rg{"Fq!پ 6Uz7cRym&B?a0!/ḋSeſB42@8&C9QA"IaX2VTNpXqD4nV{ϒqD 9IyƋ03U!pdn2 {ǖSiKFy@_t7!Xv,U-(^7"՘M>Tvj=1S2FS={=)nLuuIOF=*+!Ty *YUe.gb+(oIٵ? . -_ 1pc xs,mtoyFdvK"ql]JWZYDc_V LYfzuS8g$olhث UwDͰ N.[@N@:N،|8Eb^i~,,Ғ[/Dujͭ1`+GFo@zgr|+ Y#[XlV ASOt%5uÖ4(ȅpHb瘰 z_e \׈ԕ`ΘΚ2DNS=[aWo ,3w6lۈ7L!!KP%' itcxn{ᑞ4گ:ʈ?Gk'$-p ‡|6ѧ3}a̎%IUq\OP [wM 6ܖpCL<#eaԿ`_vɱ_ 'epP[| "Y#B5Tx/mb۷y%&U0ose J.oF 'yy9 rOVí*U*GK!0ps2 7*깃V[w^aHƉ|Eg6M6<J1D׾2ob\|Be #*S݂ol85ҭ%ㅒ re Z'j\5bIJ=ahRleJE}FywX1S6oh~avw2+!n}l `Ȑ8% JVx1UQG8tVaD0X̅\g=W_|P[p lX.F p-F&ٍ_( 254YRs[XFN5:lxA0mNq \u@~t?p2 9թ^W*q`3KEMVS3ɑ3dNYF-|8Y+:USЙ3(QO)"~ x>2Os%!_z]y3:Uwz̴0J?w]rΝYǣ/%,Jpһ*k Pc=^[נjGcV*#VY˕z1vfքiw~֒KK_ ,5\u߯c]Of]v \C* WuH5;$^D1XX$iETf!a}1s*w4Ch{s=MYTd,9Mb+RLω(fN Ln\Kownw ncd_,Kn6$2wpLNVf) φvOZD u}OCVQ% ,Lbjly31x**3,B}#rN%8`(< su`E?-DjyBC|?}Rh* q§ʳC݅Bb "u0'ioI^f>3yͯ`S%&d*Ћ|ްȏAo-i)mLCGeW32`w 65Jz7% Gdu(:~/V5C?9Zu劌 #& &v5Ҍ9#%4ʠB| *Aeu榖Fڅs:RttFT/VUUZJ<311N|\x01<5O\/ $M$ҔZWqV3Übi+lo+Cз |`S7cg;-m hro}UsΧW[Ja\[sZo3mft#>$)W~ o_0ƧgH먉go)ؘvHR9/49#,0$I:}5|{('j=,bי j!nl?Lo/ǦHyeW5gG>wi[e)FP[@zzb J)Fg'Ɇ8Yٴn@E3wYSe6hs #a2\dMxώWϊC;¸h5zB'm{ #VW;RisLcēo%+Z=STʸûv+?ظ٩YX 8L;ѱ;u8~jEhX5xMm&JCf HBug֗Ye4n0iT$eǷj\~YCV 9/bD0-bDiEQ`gxZ g֎VN'At{Αp&JBE|G=P)OFJЧÍkXta{I"1UibwLG,f@T^7OY=6A_QK'з`rG2P'tyA=ְ p:81F|f%fm+ o v49?_VvEY Ct!y|A̵Sd-l-//QyZ\ϧkEΓ{q; n}g势]!4eyuخ>\eE㭌TLdu5l= ,Bf_垑Ҵ5 > n^?H3 ЊB ,rjRZWF!n%=q1>trs<8<GcMivTCZ%ЛcE3~Wyc11eu2Jn'>_.qмYPALFKbz++&}7ŻۣcaEEhLEfOI M*O>ot08gxwH4zhThLOHA%XHmtH1"x .|8\FXc3z_eZǎmMXaeް5X́' T_Ԇp@rϤq݅ޥkr_:AT0|ٓlIKN$Nc ³r7<ȧG^;H4o,TR+ja>w|_wѮ46yq*V~(FH;.lGj[7KNrg7gFكV51zV9@$W;|29ڒ" +~УL.pyۼy쑍(#X:z{foK!YX֒\TrzRgprqTz{ T:Dh,؅˝i-d7G0rKUJXz"C;fñgGC*Wg)ACT\)#Af|HD몎%,x0[ſo\U(ʬ'lZ 昻~MT:{%.^S s4Y~GiX 99sJ-p,"uz@ڧ}i&&1ӭBadB 14yL/0cɼ) fG:u& ](`YT X*9{1A9ʝfG3JAV'St tߠ1,R *J*?¡Ӝ,СA&8%A>}+.5hT\JiHd`W,9#T3omdSPfJ"NJwDbO}pEͲ(.y!G(Ѝs[?F<+-:l7On[ebePe{$q/0+0J{z%3;ѸI 0Ck%= KB?wЬe NFwyRN YG)uxNAubRw|/c?ހ{.*-M8Q9o{P)/Bt9e 3Պ 1M!X~F HjvX\x,& \MhLŴf5 yCCo-O@'R&T&[TVCakK5X)=+\K FƘ_}R^Э;n&S ` J703:w$~B1B7"֍_-źZ'@kbq 4. U]Zm4lr1\RQ-f_=j&zԁ/njo0Ns: tvD(27>P~qzY XܕUGo,4HYp_bKD"b.0{xӀl7uzc B">~)SbE ɀX8U!kCTKt a9zV|/%~:z,O: '((=11!Wܹ53}mZEI>ulk^'#y|tg?o4;EG*(@d^ܑsOuIͶζ"`1ް1݇߱ 53?$SPPge+(Mj8ֈw)C:%ԧLƚ;O+2pm=-X\^QBJ&pdV)!=hq:GtRF]e7MAO=zKG#3xM qǸ`DC<]Y1/tom;cO$#J477%Ɩw88KR:>8)3w&[9d14yTaHˠ+^t =q}ZO7;˕Aa,$7# X,sv4e4UD7oM"&/MɵC_NFx`| tx40*K1ƑHgz>W8j"R`OV*Hچ(d"@1%c&|*e[Mgf`[P ~ʟYnlT]8G3fGG͹T -G^c*yM4,b4W1o0W|_w,R3 -S< m1!a&IfVg7(a#,HNJ`(Ig PLTKp1oی,DE-b509-WF+6b[@;tF3W ^e6J &F =i˩ ڬs xϓ\Dv+ ~m\h0#}{T%NM.=NQkuF>j!f_`],B&"G6݌o 98ꮻ֛duҹ*FJȉmʰPBpM?z]% nf gFB_U6: P^M-JaKyxZ9uKҖ6 ZKܧ\"^\cj[a 7;jְ+lL t %L+4:rE/Nd :tSX7n}a#!P~Pb'jFرHF܌s?ܴ;P9qQ ,26iAK}>w]_bu{>T̔Tb"6݂А|q{"iHZX @^wu+cɩ<#a~W0gҳpNT)̫9 gT͌t|R߄vo̷ @RhLIS1qۂhq@~+p endstream endobj 122 0 obj << /Producer (pdfTeX-1.40.16) /Creator (TeX) /CreationDate (D:20160414173823+02'00') /ModDate (D:20160414173823+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015) kpathsea version 6.2.1) >> endobj 11 0 obj << /Type /ObjStm /N 84 /First 696 /Length 3910 /Filter /FlateDecode >> stream x\]s7}ׯڊf Tʒ-G-{%ٱ-m&7ɯ$#JI>$UR 0@AبZi!2Q:*A9qg[RP֨d2Yew,NZYtֵSJzhdC)sVڳ )V{# I 61 P+dRL蟭߉PC(]R)QF؋񨇊v*bJ "T9DSp; :)|@==6@Ŭ *fɲ&? ]kBiZn!|<#@kȋOM;j/`$YkW[vpT0 6anƸ@0@!€Ȯ;.7†<5 ulW Q Ak)g?)0^LTnSqըcS.t,x:n&Y3Atsų|8~Q0]9_<FS 騅Y1f)b5Ya0+m_PsW/ƣf~Tu>Ufh=F6zn\)6Sn'd Pt!`DlǀvP*),$`jZ˦z<]_і07'j뫓t<\Cgjz|5ǏTuV hJİX774ggVt waӋp6fe0|}+۰ҙe+]}/+ٮM=:R>(t[!q=FXԁ.Ҁeh3}W}XR[eƻф[s͜ґIbGoE5eÂa!nŰ礟ջn'P.0" 5Z}wi#]j2vCqGwsT-F֡E?f?k!݈m.ҥ"FxRn{=dnci ZJWx\cƦ=#31-k)RIYFhgіF*/gY}y+WȢZySj4kfɭ/1IQk(KemaDj!(-XQw'RR.Lyk"Ou+GD6YJm @qEP%,'ʾ62*1[mH϶cfFǖ^,p>Ӳ.!ԗ`IkBZQו}:.-IjIe(>3"k.dPܜQ(V姿H"9!H/e6,S^)=GmOijaH-Dcd͇DG )S:%>~ض,=ej 3=DDX^n[@ UʞX(#H}ԃ6C[-gl_8;.gQ8\Yi)L6stx[|2!#絑;([IHȜ$N$)\ʒ?ĭ=][XHYER<ӴDork$)KbyO@N~|=R2j7JRv򼑔qDFG+Y 0 ѵVL\K &5Bvt;~M"oxI H'hUKYeUFt%9]*[R7$m6ּ;8 ֜pYZ72HsA|k&mm{(mOk-_Hy%/HɚQӎ#RLfhd}Ђ%R3i6]CjPb麐[{n([|oLëh,Gh99~գ?{v .'I=j&۩LR?f>&S4bxETLϯ;)g54?^_U'목>WVq5?Q`R(E.;Oߞыp5drM "}0!3Q |%/v5OtXk yx{ǩ_ VH-SJ;9 ƘmcIwq~<82 e;>$yL&H]A/3d7y3mD߾9y|"]\[ vG̯)lNmV@-߆Zk:y /=_޴rʘog@{@N-LSA3j?mWO>bnzyC!4)Omaoxty6:^~ļzעu%SF+NPٚNeݼq6*Kq3\^~ۮKYxju˝yK39D\%|rAzedrKL-L l9A]?F*K{I_6]tvB@o1 Ǔ) t~O ymǥ9vC^Y/M;;Swf3rf03:?~yrz_KB wΌ]Ե_-- ?K}gp-ӿC $ B-(?/+@-[P^V6wJ c# b܂BsBԶ2pYۈgY^PQhނB Y^PGj F eJ[PK/+PO ѻ]z2OȄ=29(o cr廯K^N^&ѽF׫FGWՖ,2#C'C7ѡ endstream endobj 123 0 obj << /Type /XRef /Index [0 124] /Size 124 /W [1 3 1] /Root 121 0 R /Info 122 0 R /ID [<31FA714A44183007F437A49CA9A0FB34> <31FA714A44183007F437A49CA9A0FB34>] /Length 350 /Filter /FlateDecode >> stream x%й/a3ޙ]scͺD(DD#T?(HD d[ё&_K " k##d}o{$Xg61;sLQ2E<ā BB:[Br~twZ ˠH1 ǵ҉C J2ILpU "3=!0 :H.L׾2D$fZ,RHH~RBJI)'TjRC¤>0OZ 0'-Z0 X+u ^v< ciu?juygkZC^GZ}d,} ħ8 endstream endobj startxref 585187 %%EOF kissplice-2.4.0-p1/INSTALL000644 000765 000024 00000000026 12575016362 015156 0ustar00ishistaff000000 000000 See doc/user_guide.pdfkissplice-2.4.0-p1/kissplice.py000755 000765 000024 00000137050 12703732072 016474 0ustar00ishistaff000000 000000 #!/usr/bin/env python # *************************************************************************** # # KisSplice # de-novo calling alternative splicing events from RNA-seq data. # # *************************************************************************** # # Copyright INRIA # contributors : Vincent Lacroix # Pierre Peterlongo # Gustavo Sacomoto # Alice Julien-Laferriere # David Parsons # Vincent Miele # # pierre.peterlongo@inria.fr # vincent.lacroix@univ-lyon1.fr # # This software is a computer program whose purpose is to detect alternative # splicing events from RNA-seq data. # # This software is governed by the CeCILL license under French law and # abiding by the rules of distribution of free software. You can use, # modify and/ or redistribute the software under the terms of the CeCILL # license as circulated by CEA, CNRS and INRIA at the following URL # "http://www.cecill.info". # As a counterpart to the access to the source code and rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors have only limited # liability. # In this respect, the user's attention is drawn to the risks associated # with loading, using, modifying and/or developing or reproducing the # software by the user in light of its specific status of free software, # that may mean that it is complicated to manipulate, and that also # therefore means that it is reserved for developers and experienced # professionals having in-depth computer knowledge. Users are therefore # encouraged to load and test the software's suitability as regards their # requirements in conditions enabling the security of their systems and/or # data to be ensured and, more generally, to use and operate it in the # same conditions as regards security. # # The fact that you are presently reading this means that you have had # knowledge of the CeCILL license and that you accept its terms. import os import re import sys import time import shlex import struct import shutil import os.path import tempfile import argparse import threading import multiprocessing from random import randint from operator import itemgetter from subprocess import Popen, PIPE, STDOUT TIMEOUT=100000 logFile = 0 logFileName = "" unfinished_bccs = [] num_snps = {} # print str to the logFile def printlg (*args): global logFile print >> logFile, ''.join(str(arg) for arg in args) # get the timestamp as string def getTimestamp(): return "["+time.strftime("%H:%M:%S")+" "+time.strftime("%d/%m/%Y")+"] " class Command(object): # deprecated in the future with Python3 def __init__(self): self.process = None def target(self, **kwargs): self.process = Popen(kwargs["args"], stdout=kwargs["stdout"], stderr=PIPE) com = self.process.communicate() if com[0] and (kwargs["verbose"] or self.process.returncode != 0): print com[0] # Prints stderr that was piped by Popen if com[1] and (kwargs["verbose"] or self.process.returncode != 0): print com[1] def run(self, command_line, out_file = "", mode = 'w', verbose = False, timeout = sys.maxint): if verbose: print getTimestamp() + "Running "+command_line args = shlex.split(command_line) if len(out_file): stdout_file = open(out_file, mode) kwargs = {"verbose":verbose, "args":args, "stdout":stdout_file} else: kwargs = {"verbose":verbose, "args":args, "stdout":PIPE} # Create a Thread object that will run "self.target" with arguments kwargs # (given in the form of keyword argument) and start it thread = threading.Thread(target=self.target, kwargs=kwargs) thread.start() # Wait for end of thread or time out thread.join( timeout ) # Check whether thread has ended or timed out # (if timed out, kill it and wait for it to actually die) if thread.is_alive(): self.process.terminate() thread.join() if len(out_file): stdout_file.close() if self.process.returncode == -15: print >> sys.stderr, "\n\t\t *** Timeout reached! ***\n" #+ command_line elif self.process.returncode == 15: print >> sys.stderr, "\n\t\t *** Maximum number of bubbles reached! ***\n" elif self.process.returncode == -6: print >> sys.stderr, "\n\t\t *** Memory limit reached! ***\n" elif self.process.returncode == -11: print >> sys.stderr, "\n\t\t *** Problem with " + command_line.split()[0] + " ***" print >> sys.stderr, "\t\t *** Try increasing your stack size before running KisSplice executing: \"ulimit -s \" (you may first try \"ulimit -s unlimited\" if your OS accepts it. Otherwise, you can find the highest value for executing \"ulimit -H -s\").***\n" sys.exit(self.process.returncode) elif self.process.returncode != 0: print >> sys.stderr, "Problem with " + command_line.split()[0] sys.exit(self.process.returncode) def mkdirTmp(tmpdir=None): if not tmpdir: workdir = tempfile.mkdtemp(prefix="kissplice.") else: workdir = tempfile.mkdtemp(prefix="kissplice.", dir=tmpdir) return workdir def cleanTmp(workdir): shutil.rmtree(workdir) def subprocessLauncher(command_line, out_file = "", mode = 'w', verbose = False, timeout = sys.maxint): command = Command() command.run(command_line, out_file, mode, verbose, timeout) return command.process.returncode def to_file(readfiles, filename = "tmp"): f = open(filename, 'w') reads = readfiles.split(' ') for r in reads: f.write(r + "\n") f.close() # Run debruijn graph construction def build_graph(KISSPLICE_INSTDIR, workdir, readfiles, kval, graphfile, min_cov, genome_size, verbose = False): print getTimestamp() + "--> Building de Bruijn graph..." printlg(getTimestamp() + "--> Building de Bruijn graph...") print "Graph will be written in "+graphfile+".[edges/nodes]" printlg("Graph will be written in "+graphfile+".[edges/nodes]") all_read_files = workdir + "/all_read_filenames" to_file(readfiles, all_read_files) command_line = KISSPLICE_INSTDIR+"/ks_debruijn4 " + all_read_files+" "+str(kval)+" "+str(min_cov)+" "+str(genome_size)+" "+ workdir + "/graph --kissplice --4bloom" subprocessLauncher(command_line, verbose=verbose) shutil.move(workdir+"/graph.edges", graphfile + ".edges") shutil.move(workdir+"/graph.nodes", graphfile + ".nodes") shutil.move(workdir+"/graph.solid_kmers_binary_with_count", graphfile + ".solid_kmers_binary_with_count") print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") #Run error_removal for the graph (overwrite edge file) def error_removal(KISSPLICE_INSTDIR, workdir, kval, graphfile, nobuild, cutoff, keep_counts, verbose = False): print "\n" + getTimestamp() + "--> Removing sequencing errors..." printlg("\n" + getTimestamp() + "--> Removing sequencing errors...") #checks if user passed the graph for KisSplice. In this case, maybe the error_removal step is already done if nobuild: #checks if the file created by the error_removal step is already done edge_suffix = "_C"+str(cutoff)+".edges" if os.path.isfile(graphfile+edge_suffix): print "Sequencing-errors-removal step skipped: using previously computed file "+graphfile+edge_suffix printlg("Sequencing-errors-removal step skipped: using previously computed file "+graphfile+edge_suffix) print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") return True #here we do not have the sequencing-error-removal file to use, try to create it #checks if the .counts is not yet created if os.path.isfile(graphfile+".counts") == False: #.counts file does not exist - try to create it if verbose: print "File "+graphfile+".counts not found. Creating it..." # convert binary count file to text if os.path.isfile(graphfile+".solid_kmers_binary_with_count"): fin = open(graphfile+".solid_kmers_binary_with_count", "rb") kmer_nbits = struct.unpack("i",fin.read(4))[0] k = struct.unpack("i",fin.read(4))[0] fout = open(graphfile+".counts", "w") try: while True: kmer_binary = struct.unpack('B'*(kmer_nbits / 8), fin.read( kmer_nbits / 8) ) abundance = struct.unpack( 'I', fin.read(4) ) [0] kmer = "" for i in xrange(k): kmer = "ACTG"[(kmer_binary[i/4] >> (2*(i%4)) ) % 4] + kmer print >> fout, kmer, abundance except: fin.close() fout.close() else: print "File "+graphfile+".solid_kmers_binary_with_count not found. Skipping error removal step." printlg("File "+graphfile+".solid_kmers_binary_with_count not found. Skipping error removal step.") print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") return False #now the .counts file may have been created. Run the error-removal if os.path.isfile(graphfile+".counts"): command_line = KISSPLICE_INSTDIR+"/ks_error_removal "+graphfile+".edges "+graphfile+".nodes "+str(kval)+" "+graphfile+".counts "+str(cutoff)+" "+graphfile+"_C"+str(cutoff) subprocessLauncher(command_line, verbose=verbose) if keep_counts == False: delete_counts(graphfile, verbose) print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") return True else: print "File "+graphfile+".counts could not be found/created. Skipping error removal step." printlg("File "+graphfile+".counts could not be found/created. Skipping error removal step.") print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") return False #Removes the .counts and file def delete_counts(graphfile, verbose = False): if os.path.isfile(graphfile+".counts"): if verbose: print "Removing "+graphfile+".counts" os.remove(graphfile+".counts") #Run the modules on the graph def run_modules(KISSPLICE_INSTDIR, workdir, graphfile, kval, cutoff, verbose = False, output_context = False, exec_error_removal = False): if not os.path.exists(workdir+"/bcc"): os.mkdir(workdir+"/bcc") print "\n" + getTimestamp() + "--> Finding the BCCs..." printlg("\n" + getTimestamp() + "--> Finding the BCCs...") edge_suffix = ".edges" if exec_error_removal: edge_suffix = "_C"+str(cutoff)+".edges" command_line = KISSPLICE_INSTDIR+"/ks_run_modules "+graphfile+edge_suffix+" "+graphfile+".nodes "+str(kval)+" "+workdir+"/bcc/graph" if output_context: command_line += " --output-context" return_code = subprocessLauncher(command_line, verbose=verbose) if (return_code != 0): print "\t\t *** Try increasing your stack size before running KisSplice executing: \"ulimit -s \" (you may first try \"ulimit -s unlimited\" if your OS accepts it. Otherwise, you can find the highest value for executing \"ulimit -H -s\").***\n" print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") def count_alreadyfoundSNPs(workdir): global num_snps info_snp_file = open(workdir+"/bcc/graph_info_snp_bcc", 'r') info_snp = info_snp_file.readlines() for bcc_snp in info_snp: info = bcc_snp.split()# format: bcc_id num_snps num_snps[info[0]] = int(info[1]) info_snp_file.close() def find_bcc_ids_ordered_by_size(workdir, min_length = 4): f = open( workdir+"/bcc/graph_info_bcc") bccnum2size = f.readlines()[2:] bccnumorderedbysize = [int(e[0])+1 for e in sorted(enumerate([int(t.split()[1]) for t in bccnum2size]), key=lambda x:x[1], reverse=True) if int(e[1]) >= min_length ] f.close() return (bccnum2size, bccnumorderedbysize) def enumerate_all_bubbles(KISSPLICE_INSTDIR, workdir, outdir, kval, bval, output_snps, min_edit_dist, max_cycles, UL_MAX, LL_MAX, LL_MIN, timeout, nbprocs=1, verbose = False, output_context = False, output_path = False, output_branch_count = False, experimental = False, max_memory = 0): print "\n" + getTimestamp() + "--> Enumerating all bubbles..." printlg("\n" + getTimestamp() + "--> Enumerating all bubbles...") if os.path.isfile(workdir+"/all_bcc_type0_"+str(kval)): os.remove(workdir+"/all_bcc_type0_"+str(kval)) if os.path.isfile(workdir+"/all_bcc_type1234_"+str(kval)): os.remove(workdir+"/all_bcc_type1234_"+str(kval)) f = open(workdir+"/bcc/graph_info_bcc") n_bcc = int(f.readline()) f.close() file2size = {} # filling num_snps count_alreadyfoundSNPs(workdir); # ordering bcc by decreasing size and filtering if <4 nodes bccnum2size, bccnumorderedbysize = find_bcc_ids_ordered_by_size(workdir, 4) if verbose: if len(bccnumorderedbysize) != len(bccnum2size): print "Less than 4 nodes, cannot contain a bubble!" # multiprocessing step- BEGIN pool = multiprocessing.Pool(nbprocs) TASKS = [] for i in bccnumorderedbysize: TASKS += [(enumerate_bubbles_core, i, KISSPLICE_INSTDIR, workdir, outdir, kval, bval, output_snps, min_edit_dist, max_cycles, UL_MAX, LL_MAX, LL_MIN, timeout, verbose, output_context, output_path, output_branch_count, experimental, max_memory)] imap_unordered_it = pool.imap_unordered(eval_func_tuple, TASKS, 1) for x in imap_unordered_it: if x != -1: unfinished_bccs.append(x); pool.close() pool.join() # multiprocessing step - END destinationSNPS = open(workdir+"/all_bcc_type0_"+str(kval), 'wb') ## THE FILE CONTAINS SNPS destination1234 = open(workdir+"/all_bcc_type1234_"+str(kval), 'wb') ## THE FILE CONTAINS other bcc for file in os.listdir(workdir): if file[0:17] == "tmp_all_bcc_type0": shutil.copyfileobj(open(workdir+"/"+file, 'rb'), destinationSNPS) if file[0:20] == "tmp_all_bcc_type1234": shutil.copyfileobj(open(workdir+"/"+file, 'rb'), destination1234) destinationSNPS.close() destination1234.close() if output_path: destination_paths = open(workdir+"/all_paths_k"+str(kval), 'wb') for file in os.listdir(workdir): if file[0:18] == "tmp_all_paths_bcc_": shutil.copyfileobj(open(workdir+"/"+file, 'rb'), destination_paths) destination_paths.close() f = open(workdir+"/all_bcc_type0_"+str(kval)) size0 = sum(1 for line in f) f.close() f = open(workdir+"/all_bcc_type1234_"+str(kval)) size1234 = sum(1 for line in f) f.close() n_bubbles = (size0 + size1234)/4 print "Total number of bubbles found: ", n_bubbles printlg("Total number of bubbles found: ", n_bubbles) print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") def enumerate_bubbles_core(i, KISSPLICE_INSTDIR, workdir, outdir, kval, bval, output_snps, min_edit_dist, max_cycles, UL_MAX, LL_MAX, LL_MIN, timeout, verbose = False, output_context = False, output_path = False, output_branch_count = False, experimental = False, max_memory = 0): if verbose: print "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" print "Enumerating bubbles in biconnected component "+str(i) infofile = workdir+"/bcc/graph_info_bcc" contents_file_edges = workdir+"/bcc/graph_contents_edges_bcc" contents_file_nodes = workdir+"/bcc/graph_contents_nodes_bcc" basename_edges = workdir+"/bcc/graph_all_edges_bcc" basename_nodes = workdir+"/bcc/graph_all_nodes_bcc" # Contains -1 if the process finished or the bcc number if it timed out. flag = -1 # already num_snps found - it is also the starting number from enumerating cycle num_snps_bcc = 0 if str(i) in num_snps: num_snps_bcc = num_snps[str(i)] command_line = KISSPLICE_INSTDIR+"/ks_bubble_enumeration "+ infofile+" "+ contents_file_edges+" "+ contents_file_nodes+" "+ basename_edges+" "+ basename_nodes\ +" "+str(i) \ +" "+str(kval)+" "+workdir+"/bcc/tmp_bcc_sequences_"+str(kval)+"_"+multiprocessing.current_process().name+" "+str(min_edit_dist) \ +" bcc_"+str(i) + " " + str(num_snps_bcc) + " -u "+str(UL_MAX) \ +" -L "+str(LL_MAX)+" -l "+str(LL_MIN)+" -M "+str(max_cycles)+" -s "+str(output_snps) if output_context: command_line += " -c" if output_path: command_line += " -p" if bval is not None: command_line += " -b" + str(bval) if output_branch_count: command_line += " -v" if experimental: command_line += " -e " + str(max_memory) process_returncode = subprocessLauncher(command_line, verbose=verbose, timeout=timeout) # Store the bcc number if it timed out (return code -15) OR the maximum number of bubbles was reached (return code 15) OR the memory limit was exceeded (return code -6) if process_returncode == -15 or process_returncode == 15 or process_returncode == -6: flag = i # Always append the results if the enumeration, even when it's incomplete. command_line_type0 = KISSPLICE_INSTDIR+"/ks_clean_duplicates " + workdir + "/bcc/tmp_bcc_sequences_" + str(kval) +"_"+multiprocessing.current_process().name+ "_type0.fa" command_line_type1234 = KISSPLICE_INSTDIR+"/ks_clean_duplicates " + workdir + "/bcc/tmp_bcc_sequences_" + str(kval) +"_"+multiprocessing.current_process().name+ "_type1234.fa" subprocessLauncher(command_line_type0, workdir+"/tmp_all_bcc_type0_"+str(kval)+"_"+multiprocessing.current_process().name, 'a', verbose=verbose) # append ALL BCC IN THE SAME FILE subprocessLauncher(command_line_type1234, workdir+"/tmp_all_bcc_type1234_"+str(kval)+"_"+multiprocessing.current_process().name, 'a', verbose=verbose) # append ALL BCC IN THE SAME FILE if output_path: command_line = "cat "+workdir+"/bcc/tmp_bcc_sequences_" + str(kval) +"_"+multiprocessing.current_process().name+ ".path" subprocessLauncher(command_line, workdir+"/tmp_all_paths_bcc_"+str(kval)+"_"+multiprocessing.current_process().name, 'a', verbose=verbose) # append ALL BCC IN THE SAME FILE if verbose: print "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" return flag def eval_func_tuple(f_args): return f_args[0](*f_args[1:]) def concatenate_graph_all_log_bcc_to_all_bcc_type0(workdir, kval, output_snps): if output_snps==2: #concatenate all non-branching snps destinationSNPS = open(workdir+"/all_bcc_type0_"+str(kval), 'ab') ## THE UNIQUE FILE ALSO CONTAINS SNPS shutil.copyfileobj(open(workdir+"/bcc/graph_all_log_bcc", 'rb'), destinationSNPS) destinationSNPS.close() elif output_snps==1: #concatenate only non-branching Type-0a destinationSNPS = open(workdir+"/all_bcc_type0_"+str(kval), 'ab') ## THE UNIQUE FILE ALSO CONTAINS SNPS #append the Type_0a bubbles to the destinationSNPS file snpsFile = open(workdir+"/bcc/graph_all_log_bcc", 'r') writeLine = False for line in snpsFile.readlines(): if writeLine == True: destinationSNPS.write(line) writeLine = False else: if ("Type_0a" in line): destinationSNPS.write(line) writeLine = True else: writeLine = False destinationSNPS.close() def check_read_coverage_and_sort_all_bubbles(KISSPLICE_INSTDIR, readfiles, workdir, outdir, kval, output_snps, infix_name, countsMethods, minOverlap, substitutions, verbose = False): print "\n" + getTimestamp() + "--> Computing read coherence and coverage..." printlg("\n" + getTimestamp() + "--> Computing read coherence and coverage...") # Two KisSreads executions, one for type 0 one for type 1234 # Du to the k extension, anchor should be of size k+1 for SNP if output_snps > 0: commandLineType0 = KISSPLICE_INSTDIR+"/ks_kissreadsSNPS "+workdir+"/all_bcc_type0_"+str(kval)+" "+readfiles+" -i 5 -S 25 -O "+str(kval+minOverlap)+" -o "+ workdir+"/coherentType0.fa -u "+workdir+"/uncoherentType0.fa -d " + str(substitutions) + " -c 1 -n" subprocessLauncher(commandLineType0, verbose=verbose) # no n options anymore commandLineType1234 = KISSPLICE_INSTDIR+"/ks_kissreadsSplice "+workdir+"/all_bcc_type1234_"+str(kval)+" "+readfiles+" -i 5 -k "+str(kval)+" -S 25 -O "+str(kval+minOverlap)+" -o "+workdir+"/coherentType1234.fa -u "+workdir+"/uncoherentType1234.fa -d " + str(substitutions) + " -c 1 -j " + countsMethods +" -l " + str(minOverlap) subprocessLauncher(commandLineType1234, verbose=verbose) commandLineCat = "cat " + workdir+"/uncoherentType1234.fa " if output_snps > 0: commandLineCat += workdir+"/uncoherentType0.fa " subprocessLauncher(commandLineCat, workdir + "/uncoherent.fa", "a", verbose=verbose ) print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") print getTimestamp() +"--> Sorting all bubbles..." printlg(getTimestamp() +"--> Sorting all bubbles...") nb = [0]*6# counter of number of events of each type found eventsName = ["type_0a", "type_0b", "type_1", "type_2", "type_3", "type_4"] cofilel = [] for i in range(0,6): cofilel.append(open(outdir+"/results_"+infix_name+"_coherents_"+eventsName[i]+".fa", 'w')) if output_snps > 0: snpsFile = open(workdir+"/coherentType0.fa", 'r') l = snpsFile.readlines() l.sort( reverse = True ) snpsFile.close() for event in l: eventSplitted = event.split()[-1].replace(';','\n') try: if ("Type_0a" in eventSplitted): cofilel[0].write(eventSplitted+"\n")#Transform to Fasta type nb[0] += 1 else: cofilel[1].write(eventSplitted+"\n")#Transform to Fasta type nb[1] += 1 except: pass # handling coherent "other" cofile = open(workdir+"/coherentType1234.fa", 'r') l = cofile.readlines() l.sort(reverse=True) cofile.close() retype = re.compile('Type_\d+') for event in l: try: type = retype.search(event).group() for i in range(2,6): if (type=="Type_"+str(i-1)): cofilel[i].write(event.split()[-1].replace(';','\n')+"\n")#Transform to Fasta type nb[i] += 1 except: pass for i in range(0,6): cofilel[i].close() uncofile = open(workdir+"/uncoherent.fa", 'r') uncofileout = open(outdir+"/results_"+infix_name+"_uncoherent.fa", 'w') for event in uncofile.readlines(): uncofileout.write(event.split()[-1].replace(';','\n')+"\n") uncofile.close() uncofileout.close() print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") return nb def sort_all_bubbles(KISSPLICE_INSTDIR, readfiles, workdir, outdir, kval, output_snps, infix_name, shouldDoReadCoherence, verbose = False): print "\n" + getTimestamp() + "--> Starting Bubble Output Module" printlg("\n" + getTimestamp() + "--> Starting Bubble Output Module") if shouldDoReadCoherence: outdir = outdir+"/results_without_read_coherency" if not os.path.exists(outdir): os.mkdir(outdir) print "Before checking for read coherency, all bubbles will be written to folder " + outdir printlg("Before checking for read coherency, all bubbles will be written to folder " + outdir) print "This enables you to access them even before the read coherency module finishes, which can take a long time" printlg("This enables you to access them even before the read coherency module finishes, which can take a long time") print getTimestamp() + "--> Sampling bubbles by type..." printlg(getTimestamp() + "--> Sampling bubbles by type...") concatenate_graph_all_log_bcc_to_all_bcc_type0(workdir, kval, output_snps) retype = re.compile('Type_\d+') eventsName = ["type_0a", "type_0b", "type_1", "type_2", "type_3", "type_4"] filel = [] for i in range(0,6): filel.append(open(outdir+"/results_"+infix_name+"_"+eventsName[i]+".fa", 'w')) nb = [0]*6 if output_snps > 0: snpsFile = open(workdir+"/all_bcc_type0_"+str(kval), 'r') for line in snpsFile.readlines(): if "Type_0a" in line: ofile = filel[0] nb[0] += 1 elif "Type_0b" in line: ofile = filel[1] nb[1] += 1 ofile.write(line) snpsFile.close() # handling the other type cfile = open(workdir+"/all_bcc_type1234_"+str(kval), 'r') for line in cfile.readlines(): try: type = retype.search(line).group() for i in range(1,5): if (type=="Type_"+str(i)): ofile = filel[i+1] nb[i+1] += 1 except: pass ofile.write(line) cfile.close() for i in range(0,6): nb[i] /= 2 filel[i].close() print getTimestamp() + "--> Done!" printlg(getTimestamp() + "--> Done!") print "You can now access all bubbles without read coherency in: " + outdir printlg("You can now access all bubbles without read coherency in: " + outdir) return nb def save_bccs_from_list(bcc_list, dir_name, KISSPLICE_INSTDIR, workdir, outdir, verbose = False): if not os.path.exists(outdir + dir_name): os.mkdir(outdir + dir_name) infofile = workdir+"/bcc/graph_info_bcc" contents_file_edges = workdir+"/bcc/graph_contents_edges_bcc" contents_file_nodes = workdir+"/bcc/graph_contents_nodes_bcc" basename_edges = workdir+"/bcc/graph_all_edges_bcc" basename_nodes = workdir+"/bcc/graph_all_nodes_bcc" for i in bcc_list: command_line = KISSPLICE_INSTDIR+"/ks_print_bcc "+ infofile+" "+ contents_file_edges+" "+ contents_file_nodes+" "+ basename_edges+" "+ basename_nodes\ +" "+str(i)+" "\ + outdir+ dir_name + "/graph_bcc_"+str(i)+".edges "\ + outdir+ dir_name + "/graph_bcc_"+str(i)+".nodes" subprocessLauncher(command_line, verbose=verbose) def check_read_files(readfiles): if readfiles is None: return True allFilesAreOK = True for file in readfiles: if not os.path.isfile(file): print "[ERROR] File \""+file+"\" does not exist." allFilesAreOK = False if not allFilesAreOK: dieToFatalError("One or more read files do not exist.") def dieToFatalError (msg): print "[FATAL ERROR] " + msg print "Try `kissplice --help` for more information" global logFileName os.remove(logFileName) sys.exit(1); # ############################################################################ # Main # ############################################################################ def main(): # @variable KISSPLICE_INSTDIR : Path to the main executable (this file) # This variable is initialized from CMAKE to customize this script # according to the install configuration. # @variable KS_SEC_EXEC_PATH : Path to the secondary executables (eg ks_kissreads) KISSPLICE_INSTDIR = os.path.dirname(os.path.abspath(sys.argv[0])).rstrip('/') KS_SEC_EXEC_PATH = KISSPLICE_INSTDIR.rsplit('/',1)[0]+'/@KS_SEC_EXEC_PATH@' print "KISSPLICE_INSTDIR: "+KISSPLICE_INSTDIR print "KS_SEC_EXEC_PATH: "+KS_SEC_EXEC_PATH # ======================================================================== # Manage command line arguments # ======================================================================== parser = argparse.ArgumentParser(description='kisSplice - local assembly of SNPs, indels and AS events') # ------------------------------------------------------------------------ # Define allowed options # ------------------------------------------------------------------------ parser.add_argument("-r", action="append", dest="readfiles", help="input fasta/q read files or compressed (.gz) fasta/q files (mutiple, such as \"-r file1 -r file2...\") ") parser.add_argument('-k', action="store", dest="kval", type=int, default=41, help="k-mer size (default=41)") parser.add_argument('-b', action="store", dest="bval", type=int, default=5, help="maximum number of branching nodes (default 5)") parser.add_argument('-l', action="store", dest="llmax", type=int, default=0, help="maximal length of the shorter path (default: 2k+1)") parser.add_argument('-m', action = "store", dest = "LL_MIN", default = 0, help = "minimum length of the shorter path (default 2k-8)") parser.add_argument('-M', action = "store", dest = "UL_MAX", default = 1000000, help = "maximum length of the longest path (default 1000000), skipped exons longer than UL_MAX are not reported") parser.add_argument('-g', action="store", dest="graph_prefix", default="", help="path and prefix to pre-built de Bruijn graph (suffixed by .edges/.nodes)\n \ if jointly used with -r, graph used to find bubbles and reads used for quantification") parser.add_argument('-o', action="store", dest="out_dir", default="results", help="path to store the results and the summary log file (default = ./results)") parser.add_argument('-d', action="store", dest="path_to_tmp", default=None, help="specific directory (absolute path) where to build temporary files (default temporary directory otherwise)") parser.add_argument('-t', action="store", dest="nbprocs", type=int, default=1, help="number of cores (must be <= number of physical cores)") parser.add_argument('-s', action="store", dest="output_snps", default = "0", help="0, 1 or 2. Changes which types of SNPs will be output. If 0 (default), will not output SNPs. If 1, will output Type0a-SNPs. If 2, will output Type0a and Type0b SNPs (warning: this option may increase a lot the running time. You might also want to try the experimental algorithm here)") parser.add_argument('-v', action="store_true", dest="verbose", help="Verbose mode") parser.add_argument('-u', action="store_true", dest="keep_ubccs", help="keep the nodes/edges file for unfinished bccs") parser.add_argument('-c', action = "store", dest = "min_cov", default = 2, help="an integer, k-mers present strictly less than this number of times in the dataset will be discarded (default 2)") parser.add_argument('-C', action = "store", dest = "min_relative_cov", default = 0.05, help="a percentage from [0,1), edges with relative coverage below this number are removed (default 0.05)") parser.add_argument('-z', action="store", dest="genome_size", type=int, default=1000000000, help="estimated genome/transcriptome size (default = 1G)") parser.add_argument('-e', action = "store", dest = "min_edit_dist", default = 3, help="edit distance threshold, if the two sequences (paths) of a bubble have edit distance smaller than this threshold, the bubble is classified as an inexact repeat (default 3)") parser.add_argument('-y', action = "store", dest = "max_cycles", default = 100000000, help="maximal number of bubbles enumeration in each bcc. If exceeded, no bubble is output for the bcc (default 100M)") parser.add_argument('--mismatches', action = "store", dest = "mism", default = 0, type = int, help="Maximal number of substitutions authorized between a read and a fragment (for quantification only), default 0. If you increase the mismatch and use --counts think of increasing min_overlap too.") parser.add_argument('--counts', action = "store", dest = "countsMethod", default = "0", help="0,1 or 2 . Changes how the counts will be reported. If 0 (default): total counts, if 1: counts on junctions, if 2: all counts. see User guide for more information ") parser.add_argument('--min_overlap', action = "store", dest = "minOverlap", default = 3, type=int, help="Set how many nt must overlap a junction to be counted by --counts option. Default=3. see User guide for more information ") parser.add_argument('--timeout', action='store', dest="timeout", default=TIMEOUT, help="max amount of time (in seconds) spent for enumerating bubbles in each bcc. If exceeded, no bubble is output for the bcc (default "+str(TIMEOUT)+")") parser.add_argument('--version', action='version', version='%(prog)s 2.4.0-p1') parser.add_argument('--output-context', action="store_true", dest="output_context", default = False, help="Will output the maximum non-ambiguous context of a bubble") parser.add_argument('--output-path', action="store_true", dest="output_path", default = False, help="Will output the id of the nodes composing the two paths of the bubbles.") parser.add_argument('--output-branch-count', action="store_true", dest="output_branch_count", default = False, help="Will output the number of branching nodes in each path.") parser.add_argument('--keep-bccs', action="store_true", dest="keep_all_bccs", default = False, help="Keep the node/edges files for all bccs.") parser.add_argument('--experimental', action="store_true", dest="experimental", default = False, help="Uses a new experimental algorithm that searches for bubbles by listing all paths.") parser.add_argument('--max-memory', action="store", dest="max_memory", default="unlimited", help="If you use the experimental algorithm, you must provide the maximum size of the process's virtual memory (address space) in megabytes (default unlimited). WARNING: this option does not work on Mac operating systems.") parser.add_argument('--keep-counts', action="store_true", dest="keep_counts", default = False, help="Keep the .counts file after the sequencing-errors-removal step.") # ------------------------------------------------------------------------ # Parse and interpret command line arguments # ------------------------------------------------------------------------ options = parser.parse_args() # ------------------------------------------------------------------------ # Create output dir # ------------------------------------------------------------------------ outdir = options.out_dir if not os.path.exists(outdir): os.mkdir(outdir) # ------------------------------------------------------------------------ # Create the log file # ------------------------------------------------------------------------ global logFile, logFileName logFileName = outdir+"/kissplice_log_summary_"+time.strftime("%H-%M-%S")+"_"+time.strftime("%d-%m-%Y")+"_"+str(randint(0, 1000000)) logFile = open(logFileName, 'w') # ------------------------------------------------------------------------ # Print version and command line # ------------------------------------------------------------------------ print "\nThis is KisSplice, version 2.4.0-p1\n" printlg("This is KisSplice, version 2.4.0-p1\n") print "The command line was: " + ' '.join(sys.argv) printlg("The command line was: " + ' '.join(sys.argv)) # ------------------------------------------------------------------------ # Parse input options # ------------------------------------------------------------------------ # check if the given read files indeed exist check_read_files(options.readfiles) readfiles = None only_graph = False if options.readfiles: if options.graph_prefix: # GRAPH + READS print "-r and -g options used together: " printlg("-r and -g options used together: ") print "the graph will be used to find events, while reads files are used for checking read-coherency and coverage" printlg("the graph will be used to find events, while reads files are used for checking read-coherency and coverage") readfiles = ' '.join(map(str, options.readfiles)) else: if not options.graph_prefix: parser.print_usage() dieToFatalError("kissplice requires at least a read file or a pre-built graph") else: # GRAPH only_graph = True nobuild = False if options.graph_prefix: nobuild = True # --------------------------------------------------------- Output options output_snps = (int)(options.output_snps) if output_snps<0 or output_snps>2: print "-s is not 0, 1 or 2. Defaulting to 0." printlg("-s is not 0, 1 or 2. Defaulting to 0.") output_snps = 0 print "Using the read files: ", readfiles printlg("Using the read files: ", readfiles) print "Results will be stored in: ", os.path.abspath(options.out_dir) printlg("Results will be stored in: ", os.path.abspath(options.out_dir)) print "Summary log file will be saved in: ", os.path.abspath(logFileName) printlg("Summary log file will be saved in: ", os.path.abspath(logFileName)) print "\n" printlg("\n") # ------------------------------------------------------------- k-mer size kval = options.kval if kval%2 == 0: dieToFatalError("please use only odd value for k") #otherwise, DBG use k-1 and output context do not work # ------------------------------------- Maximal length of the shorter path if options.llmax != 0: LL_MAX = options.llmax else: LL_MAX = 2 * kval + 1 # The following are not optional but work along with llmax UL_MAX = options.UL_MAX # Defines maximum upper and lower path bounds if options.LL_MIN != 0: LL_MIN= options.LL_MIN else: LL_MIN = 2 * kval - 8 min_ll_max = 2 * kval + 1 if LL_MAX < min_ll_max: dieToFatalError("maximal length of the shorter path (" + str(LL_MAX) + ") should be >= 2k+1 =" + str(min_ll_max) + ")") #-------------------------------- fix LL_MIN, LL_MAX and UL_MAX -------------------------------------- LL_MIN = int(LL_MIN)-2 LL_MAX = int(LL_MAX)-2 UL_MAX = int(UL_MAX)-2 # ------------------------------------------------------- Other parameters min_cov = options.min_cov min_edit_dist = options.min_edit_dist max_cycles = options.max_cycles countsMethod = options.countsMethod minOverlap = options.minOverlap # ======================================================================== # Construct intermediate and output file names # ======================================================================== workdir = mkdirTmp(options.path_to_tmp) infix_name = "" # will be the central part of the output file names if options.graph_prefix: graphfile = options.graph_prefix if options.readfiles: for file in options.readfiles: justfilename = file.split("/")[-1].split(".")[0] #remove what is before the "/" and what is after the "." infix_name += justfilename+"_" infix_name = infix_name[0:200] # Truncate it to contain at most 200 characteres infix_name += "k" + str(kval) if not options.graph_prefix: graphfile = options.out_dir+"/graph_"+infix_name # Output graph file else: infix_name = graphfile.split("/")[-1].split(".")[0] #remove what is before the "/" and what is after the "." # ======================================================================== # RUN # ======================================================================== # ------------------------------------------------------------------------ # Build De Bruijn Graph # ------------------------------------------------------------------------ if not nobuild: t = time.time() build_graph(KS_SEC_EXEC_PATH, workdir, readfiles, kval, graphfile, min_cov, options.genome_size, options.verbose) print "Elapsed time: ", (time.time() - t), " seconds" printlg("Elapsed time: ", (time.time() - t), " seconds") # ------------------------------------------------------------------------ # Error removal # ------------------------------------------------------------------------ t = time.time() if float(options.min_relative_cov) > 0.0001: exec_error_removal = error_removal(KS_SEC_EXEC_PATH, workdir, kval, graphfile, nobuild, options.min_relative_cov, options.keep_counts, options.verbose) else: exec_error_removal = False print "Elapsed time: ", (time.time() - t), " seconds" printlg("Elapsed time: ", (time.time() - t), " seconds") # ------------------------------------------------------------------------ # Decompose and simplify DBG # ------------------------------------------------------------------------ t = time.time() run_modules(KS_SEC_EXEC_PATH, workdir, graphfile, kval, options.min_relative_cov, options.verbose, options.output_context, exec_error_removal) print "Elapsed time: ", (time.time() - t), " seconds" printlg("Elapsed time: ", (time.time() - t), " seconds") # ------------------------------------------------------------------------ # Enumerate Bubbles # ------------------------------------------------------------------------ t = time.time() enumerate_all_bubbles(KS_SEC_EXEC_PATH, workdir, outdir, kval, options.bval, output_snps, min_edit_dist, max_cycles, \ UL_MAX, LL_MAX, LL_MIN, float(options.timeout), options.nbprocs, options.verbose, \ options.output_context, options.output_path, options.output_branch_count, options.experimental, options.max_memory) print "Elapsed time: ", (time.time() - t), " seconds" printlg("Elapsed time: ", (time.time() - t), " seconds") # ------------------------------------------------------------------------ # Check read coverage (optionnal) and sort bubbles # ------------------------------------------------------------------------ t = time.time() nb = sort_all_bubbles(KS_SEC_EXEC_PATH, readfiles, workdir, outdir, kval, output_snps, infix_name, not only_graph, options.verbose) if not only_graph: nb = check_read_coverage_and_sort_all_bubbles(KS_SEC_EXEC_PATH, readfiles, workdir, outdir, kval, output_snps, infix_name, countsMethod, minOverlap, options.mism, options.verbose) print "Elapsed time: ", (time.time() - t), " seconds\n" printlg("Elapsed time: ", (time.time() - t), " seconds\n") if only_graph: print "\n\n \t\t ******** We are done, final results are in files "+outdir+"/results_"+infix_name+"_type_*.fa **********" printlg("\n\n \t\t ******** We are done, final results are in files "+outdir+"/results_"+infix_name+"_type_*.fa **********") else: print "\n\n \t\t ******** We are done, final coherent results are in files "+outdir+"/results_"+infix_name+"_coherents_type_*.fa ********** " printlg("\n\n \t\t ******** We are done, final coherent results are in files "+outdir+"/results_"+infix_name+"_coherents_type_*.fa ********** ") print " \t\t ******** All non read coherent results are in files "+outdir+"/results_"+infix_name+"_uncoherent.fa ****** \n\n" printlg(" \t\t ******** All non read coherent results are in files "+outdir+"/results_"+infix_name+"_uncoherent.fa ****** \n\n") # ------------------------------------------------------------------------ # Manage BCCs # ------------------------------------------------------------------------ if len(unfinished_bccs) != 0: print "\t\t ******** There were " + str(len(unfinished_bccs)) + " BCCs with unfinished enumeration ********" printlg("\t\t ******** There were " + str(len(unfinished_bccs)) + " BCCs with unfinished enumeration ********") if not options.keep_ubccs and not options.keep_all_bccs: print "\t\t ******** Re-run with `-u` to retrieve the unfinished components ********\n" printlg("\t\t ******** Re-run with `-u` to retrieve the unfinished components ********\n") if options.keep_ubccs: bcc_dir = "/unfinished_bcc" print "\t\t Backup files for the unfinished BCCs are in " + outdir + bcc_dir + "\n" printlg("\t\t Backup files for the unfinished BCCs are in " + outdir + bcc_dir + "\n") save_bccs_from_list(unfinished_bccs, bcc_dir, KS_SEC_EXEC_PATH, workdir, outdir, options.verbose) if options.keep_all_bccs: bcc_dir = "/bcc" print "\t\t Edge and node files of all BCCs are in " + outdir + bcc_dir + "\n" printlg("\t\t Edge and node files of all BCCs are in " + outdir + bcc_dir + "\n") all_bccs = find_bcc_ids_ordered_by_size(workdir)[1] save_bccs_from_list(all_bccs, bcc_dir, KS_SEC_EXEC_PATH, workdir, outdir, options.verbose) if options.output_path: # move paths file to outdir shutil.move(workdir+"/all_paths_k"+str(kval), outdir + "/all_paths_k"+str(kval)) # ------------------------------------------------------------------------ # Output number of events of each type # ------------------------------------------------------------------------ print "\t\t TYPES:" printlg("\t\t TYPES:") if output_snps!=0: print "\t\t\t 0a: Single SNPs, Inexact Repeats or sequencing substitution errors, "+str(nb[0])+" found" printlg("\t\t\t 0a: Single SNPs, Inexact Repeats or sequencing substitution errors, "+str(nb[0])+" found") if output_snps==2: print "\t\t\t 0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, "+str(nb[1])+" found" printlg("\t\t\t 0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, "+str(nb[1])+" found") else: print "\t\t\t 0b: Run with -s 2 to also search for Multiple SNPs (warning: this option may increase a lot the running time. You might also want to try the experimental algorithm here)" printlg("\t\t\t 0b: Run with -s 2 to also search for Multiple SNPs (warning: this option may increase a lot the running time. You might also want to try the experimental algorithm here)") else: print "\t\t\t 0: Run with -s option set to 1 or 2 to also search for SNPs" printlg("\t\t\t 0: Run with -s option set to 1 or 2 to also search for SNPs") print "\t\t\t 1: Alternative Splicing Events, "+str(nb[2])+" found" printlg("\t\t\t 1: Alternative Splicing Events, "+str(nb[2])+" found") print "\t\t\t 2: Inexact Tandem Repeats, "+str(nb[3])+" found" printlg("\t\t\t 2: Inexact Tandem Repeats, "+str(nb[3])+" found") print "\t\t\t 3: Short Indels (<3nt), "+str(nb[4])+" found" printlg("\t\t\t 3: Short Indels (<3nt), "+str(nb[4])+" found") print "\t\t\t 4: All others, composed by a shorter path of length > 2k not being a SNP, "+str(nb[5])+" found" printlg("\t\t\t 4: All others, composed by a shorter path of length > 2k not being a SNP, "+str(nb[5])+" found") print "\n\n \t\t ******** A summary of the execution can be found in the log file: " + os.path.abspath(logFileName) + "**********" printlg("\n\n \t\t ******** A summary of the execution can be found in the log file: " + os.path.abspath(logFileName) + "**********") # ------------------------------------------------------------------------ # Clean tmp directory # ------------------------------------------------------------------------ logFile.close() cleanTmp(workdir) if __name__ == '__main__': main() kissplice-2.4.0-p1/man/CMakeLists.txt000644 000765 000024 00000000066 12575016362 017444 0ustar00ishistaff000000 000000 install(FILES kissplice.1 DESTINATION share/man/man1) kissplice-2.4.0-p1/man/kissplice.1000644 000765 000024 00000007515 12703734227 016762 0ustar00ishistaff000000 000000 .TH KISSPLICE "14" "april 2016" "kissplice 2.4.0-p1" "User Commands" .SH NAME kisSplice \- local assembly of SNPs, indels and AS events .SH SYNOPSIS kissplice \-h .br kissplice [OPTION] [\-r READFILES] .SH DESCRIPTION Detects alternative splicing events and other kinds of polymorphisms from READFILES (in FASTA or FASTQ format). .SH OPTIONS .TP .B \-h, \-\-help Show this help message and exit. .TP \fB\-r\fR READFILES Input fasta/q read files or compressed (.gz) fasta/q files (mutiple, such as "-r file1 -r file2...") .TP \fB\-k\fR KVAL k\-mer size (default=41). .TP \fB\-b\fR BVAL Maximum number of branching nodes (default: 5) .TP \fB\-l\fR LLMAX Maximal length of the shorter path (default: 2k\+1). .TP \fB\-m\fR LL_MIN Minimum length of the shorter path (default 2k\-8). .TP \fB\-M\fR UL_MAX Maximum length of the longest path (default: 1000000), skipped exons longer than UL_MAX are not reported. .TP \fB\-g\fR GRAPH_PREFIX Path and prefix to pre\-built de Bruijn graph (suffixed by .edges/.nodes) if jointly used with \fB\-r\fR, graph used to find bubbles and reads used for quantification. .TP \fB\-o\fR OUT_DIR Path to store the results (default = ./results). .TP \fB\-d\fR PATH_TO_TMP Specific directory (absolute path) where to build temporary files (default temporary directory otherwise). .TP \fB\-t\fR NBPROCS Number of cores (must be <= number of physical cores). .TP \fB\-s\fR OUTPUT_SNPS 0, 1 or 2. Changes which types of SNPs will be output. If 0 (default), will not output SNPs. If 1, will output Type0a-SNPs. If 2, will output Type0a and Type0b SNPs (warning: this option may increase a lot the running time. You might also want to try the experimental algorithm here). .TP \fB\-v\fR Verbose mode. .TP \fB\-u\fR Keep the nodes/edges file for unfinished bccs. .TP \fB\-c\fR MIN_COV Discard k\-mers tha are present strictly less than this number of times in the dataset. (default 2). .TP \fB\-C\fR MIN_RELATIVE_COV Discard edges with relative coverage below MIN_RELATIVE_COV expressed as a percentage in [0,1). (default 0.05). .TP \fB\-z\fR GENOME_SIZE Estimated number of nodes in the De-Bruin Graph. (default = 1000000000). .TP \fB\-e\fR MIN_EDIT_DIST Classify as inexact repeats those bubbles whose paths' edit distance is smaller than MIN_EDIT_DIST (default 3). .TP \fB\-y\fR MAX_CYCLES Maximal number of bubble enumerations in each bcc. If exceeded, no bubble is output for the bcc (default: 100000000). .TP \fB\-\-mismatches\fR NB_MISMATCHES Maximal number of substitutions authorized between a read and a fragment (for quantification only), default 0. If you increase the mismatch and use \fB\-\-counts\fR think of increasing min_overlap too. .TP \fB\-\-counts\fR COUNTS_TYPE Changes how the counts will be reported. If 0 (default): total counts, if 1: counts on junctions, if 2: all counts. .TP \fB\-\-min_overlap\fR MIN_OVERLAP Sets how many nt must overlap a junction to be counted by --counts option (default: 3). .TP \fB\-\-timeout\fR TIMEOUT Max amount of time (in seconds) spent for enumerating bubbles in each bcc. If exceeded, no bubble is output for the bcc (default 100000). .TP \fB\-\-version\fR Display program's version number and exit. .TP \fB\-\-output-context\fR Will output the maximum non-ambiguous context of a bubble. .TP \fB\-\-output-path\fR Will output the id of the nodes composing the two paths of the bubbles. .TP \fB\-\-output-branch-count\fR Will output the number of branching nodes in each path. .TP \fB\-\-keep-bccs\fR Keep the node/edges files for all bccs. .TP \fB\-\-experimental\fR Uses a new experimental algorithm that searches for bubbles by listing all paths. .TP \fB\-\-max-memory MAX_MEMORY\fR If you use the experimental algorithm, you must provide the maximum size of the process's virtual memory (address space) in megabytes (default unlimited). .TP \fB\-\-keep-counts\fR Keep the .counts file after the sequencing-errors-removal step. .TPkissplice-2.4.0-p1/modules/BubbleEnumeration.cpp000644 000765 000024 00000070316 12611220004 021671 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #include #include #include #include #include #include #include #include #include #include "LabelledCEdge.h" #include "WeightedDigraph.h" #include "CGraph.h" #include "BubbleEnumeration.h" #include "Utils.h" #include #define MAX_DIST 10000000 #define MAX 1024 using namespace std; int nbBubbles = 0; //these 3 variables represent beta, a1 and a2. We have a1Global and a2Global so that it does not confuse with some locals a1 and a2 int beta = 0; int a1Global = 0; int a2Global = 0; int MAX_BUBBLES; int UL_MAX; int LL_MAX; int LL_MIN; int MIN_DIST; int BUBBLE_COUNT_OFFSET; int MAX_BRANCHES; int MAX_MEMORY; int OUTPUT_SNPS; bool OUTPUT_CONTEXT; bool OUTPUT_PATH; bool OUTPUT_BRANCH; bool EXPERIMENTAL_ALG; // OUTPUT FILE, One for SNPs, One for other type of Bubble FILE *seq_output_file_type0; FILE *seq_output_file_type1234; FILE *path_output_file; string comment = ""; vector seqs; int k_value; int nb_nodes; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Returns the sequence for node "i", stored in the seqs vector. There is a catch, the vector does not store the sequence of the complementary node. The function has to convert it. */ string getSeq(int i) { return (i < (int)seqs.size()) ? string(seqs[i]) : reverse_complement(string(seqs[i % (int)seqs.size()])); } /*! * \brief Return the path sequence ( with or without context) * \param path: nodes ids path * \param all_nodes : output_context boolean * \param output: if seq gotten for output, then there is one nt more on each side of the path * to detect various switching nodes */ string path2seq(vector& path, bool all_nodes, bool output) { string pseq = getSeq( path[0] ); for (int i = 1; i < (int)path.size(); i++) { pseq += getSeq(path[i]).substr(k_value-1); } if (!all_nodes) { int len_first; int len_last ; if ( output ) { len_first = getSeq( path[0] ).size() - (k_value ); len_last = getSeq( path[(int)path.size()-1] ).size() - (k_value ); } else { len_first = getSeq( path[0] ).size() - (k_value ); len_last = getSeq( path[(int)path.size()-1] ).size() - (k_value ); } return pseq.substr(len_first, (int) pseq.size() - (len_first + len_last) ); } return pseq; } void print_path(FILE* stream, vector& path) { for (int i = 0; i < (int)path.size(); i++) { fprintf(stream, " %d", path[i]); } fprintf(stream, "\n"); } void print_formated_path(FILE* stream, string& bcc, int cycle_num, string& type, string path_name, int len, string& bcount, string& seq) { fprintf(stream, ">%s|Cycle_%d|Type_%s|%s_length_%d%s\n", bcc.c_str(), cycle_num, type.c_str(), path_name.c_str(), len, bcount.c_str()); fprintf(stream, "%s\n", seq.c_str()); } void output_bubble(FILE* stream, string& upper, int upper_b, string& lower, int lower_b, string type, int num, int contextFirst, int contextLast) { // passing in lower case context if(contextFirst != 0 || contextLast != 0) { upper = toLowerContext( upper, contextFirst, contextLast); lower = toLowerContext( lower, contextFirst, contextLast); } string upper_bcount = "", lower_bcount = ""; if (OUTPUT_BRANCH) { upper_bcount = "_branches_" + to_string(upper_b); lower_bcount = "_branches_" + to_string(lower_b); } print_formated_path(stream, comment, num, type, "upper_path", (int)upper.size() - (contextFirst + contextLast), upper_bcount, upper); print_formated_path(stream, comment, num, type, "lower_path", (int)lower.size() - (contextFirst + contextLast), lower_bcount, lower); } /* * for output node-ids option */ void output_bubble(FILE* stream, vector& upper, int upper_b, vector& lower, int lower_b, string type, int num) { fprintf(stream, ">%s|Cycle_%d|Type_%s|upper_path_length_%d\n", comment.c_str(), num, type.c_str(), (int)upper.size()); print_path(stream, upper); fprintf(stream, ">%s|Cycle_%d|Type_%s|lower_path_length_%d\n", comment.c_str(), num, type.c_str(), (int)lower.size()); print_path(stream, lower); } string classify_bubble(vector& p1_nodes, vector& p2_nodes) { string upper_seq = path2seq(p1_nodes, false, false); string lower_seq = path2seq(p2_nodes, false, false); /* truncated -1 nt left and -1nt right, they only serve to distinguish Swithcing Nodes, it has nothing to do with quantification */ upper_seq = upper_seq.substr(1, upper_seq.size() - 2); lower_seq = lower_seq.substr(1, lower_seq.size() - 2); if (upper_seq.size() < lower_seq.size()) swap(upper_seq, lower_seq); string type; int u_len = (int) upper_seq.size(), l_len = (int) lower_seq.size(); if (l_len <= (2 * k_value - 2)) // lower path of at most 2k-2: splicing, repeats, and indels { int d1, d2; if ((u_len - l_len) > 0 && (((u_len - l_len) <= 2) || ((u_len - l_len) == 4) || ((u_len - l_len) == 5))) //indel type = "3"; else if ( (d1 = edit_distance(upper_seq.c_str(), l_len, lower_seq.c_str(), l_len, sizeof (char), comp)) <= MIN_DIST || (d2 = edit_distance(upper_seq.c_str() + u_len - l_len, l_len, lower_seq.c_str(), l_len, sizeof (char), comp)) <= MIN_DIST) // tandem repeat type = "2"; else //splicing type = "1"; } else if (u_len == l_len && // SNP //specific condition for Type_0a ((u_len == 2 * k_value - 1) || //specific condition for Type_0b, at most 10% differences in the variable region. Arbitrary TODO: check this (hamming_distance(upper_seq.c_str()+(k_value-1), l_len-(2*k_value)+2, lower_seq.c_str()+(k_value-1), l_len-(2*k_value)+2, sizeof (char), comp) <= (0.1 * (l_len-(2*k_value)+2))))) { if (u_len > 2 * k_value - 1) // multiple SNPs type = "0b"; else type = "0a"; } else if (l_len <= LL_MAX) // others with lower path of length bigger than 2k-2 and smaller or equal to LL_MAX type = "4"; else //here we have bubbles such that the lower path is bigger than LL_MAX and is not a Type0b. This can only happen by default if we were searching for Type0bs and a bubble is not a Type0b type = "undefined"; return type; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// set in_sub; bool contains(set &s, int elem) { return s.find(elem) != s.end(); } /* Used by the heap, as the pair . */ typedef pair ii; /* Dijsktra's algorithm. Returns the distance (shortest path) from source to every node. The results are in dist. */ void dijkstra(int max_dist, int source, map& dist, WeightedDigraph& G) { priority_queue, greater > Q; dist[source] = 0; Q.push(ii(0, source)); while (!Q.empty()) { ii top = Q.top(); Q.pop(); int v = top.second; for (int i = 0; i < (int)G.adj_list[v].size(); i++) if (!G.adj_list[v][i].removed) { int w = G.adj_list[v][i].node, cost = G.adj_list[v][i].cost; if (!G.removed[w] && contains(in_sub, w)) { if (dist.find(w) == dist.end()) dist[w] = MAX_DIST; if (dist[w] > dist[v] + cost) { dist[w] = dist[v] + cost; Q.push(ii(dist[w], w)); } } } } } /* Checks if the distance from s to t is smaller than max_dist */ bool dist_st(int max_dist, int s, int t, WeightedDigraph& G) { map dist; dist[t] = MAX_DIST; dijkstra(max_dist, s, dist, G); return dist[t] <= max_dist + G.node_cost[t]; } /* Check if there exist a pair compatible paths from s1, s2 respecting a1, a2. Check the paper for a precise definition of pair of compatible paths. */ bool testPairCompatible(int s1, int a1, int s2, int a2, WeightedDigraph& G) { map dist1, dist2; dijkstra(a1, s1, dist1, G); dijkstra(a2, s2, dist2, G); map::iterator it; for (it = dist1.begin(); it != dist1.end(); it++) if (dist2.find(it->first) != dist2.end()) { int i = it->first; if (dist1[i] <= (a1 + G.node_cost[i]) && dist2[i] <= (a2 + G.node_cost[i])) return true; } return false; } /* In-degree is equal to out-degree of the completary node, because it's a de Bruijn graph.*/ int inDegree(int node, WeightedDigraph& G) { if (node < nb_nodes) return G.outDegree(node + nb_nodes); else return G.outDegree(node - nb_nodes); } int nbBranchingNodes(vector& p_nodes, WeightedDigraph& G) { int nb = 0; // Disregard the initial and final nodes. for (int i = 1; i < (int)p_nodes.size()-1; i++) if (G.outDegree(p_nodes[i]) != 1 || inDegree(p_nodes[i],G) != 1) nb++; return nb; } void processBubbleFound(vector &p1_nodes, vector &p2_nodes, WeightedDigraph &G) { string type = classify_bubble(p1_nodes, p2_nodes); if (type != "undefined" && (type[0] != '0' || OUTPUT_SNPS)) { string upper_seq = path2seq(p1_nodes, OUTPUT_CONTEXT, true); string lower_seq = path2seq(p2_nodes, OUTPUT_CONTEXT, true); int upper_b = nbBranchingNodes(p1_nodes, G), lower_b = nbBranchingNodes(p2_nodes, G); bool swp = false; if (upper_seq.size() < lower_seq.size()) { swap(upper_seq, lower_seq); swap(upper_b, lower_b); swp = true; } FILE *selection = (type[0] == '0') ? seq_output_file_type0 : seq_output_file_type1234; // Writting in different files if type 0 or else if (OUTPUT_CONTEXT) { // getting the context: size of the first (or last ) node - (k-1) right int contextLeft = getSeq(p1_nodes[0]).size() - (k_value); int contextRight = getSeq(p1_nodes[(int) p1_nodes.size() - 1]).size() - (k_value); output_bubble(selection, upper_seq, upper_b, lower_seq, lower_b, type, BUBBLE_COUNT_OFFSET + nbBubbles, contextLeft, contextRight); } else { output_bubble(selection, upper_seq, upper_b, lower_seq, lower_b, type, BUBBLE_COUNT_OFFSET + nbBubbles, 0, 0); // size of the context if null } if (OUTPUT_PATH) { output_bubble(path_output_file, swp ? p2_nodes : p1_nodes, upper_b, swp ? p1_nodes : p2_nodes, lower_b, type, BUBBLE_COUNT_OFFSET + nbBubbles); } } nbBubbles++; if (nbBubbles > MAX_BUBBLES) exit(15); } /*! * \brief list all pair of compatible paths (see paper), classify the events found * and output them * \param s1 * \param a1 * \param p1 * \param p1_nodes * \param s2 * \param a2 * \param p2 * \param p2_nodes * \param G * * * List all pair of compatible paths from s1,s2 respecting a1,a2. It's a recursive algorithm based on the bipartition method. Check the paper for a full description. */ void listPairCompatible(int s1, int a1, int p1, vector& p1_nodes, int s2, int a2, int p2, vector& p2_nodes, WeightedDigraph& G) { if (nbBranchingNodes(p1_nodes, G) > MAX_BRANCHES || nbBranchingNodes(p2_nodes, G) > MAX_BRANCHES) return; if (s1 == s2 && (p1_nodes.size() != 1 || p2_nodes.size() != 1)) { if ( p1 >= (beta + G.node_cost[s1]) && p2 >= (beta + G.node_cost[s2]) ) { processBubbleFound(p1_nodes, p2_nodes, G); } return; } if (G.adjListSz(s1) == 0 && G.adjListSz(s2) == 0) return; int u = (G.adjListSz(s1) != 0) ? s1 : s2; G.removed[u] = true; for (int i = 0; i < (int)G.adj_list[u].size(); i++) { int v = G.adj_list[u][i].node, cost = G.adj_list[u][i].cost; if (!G.removed[v] && !G.adj_list[u][i].removed && contains(in_sub, v)) { if (u == s1 && testPairCompatible(v, a1 - cost, s2, a2, G)) { p1_nodes.push_back(v); listPairCompatible(v, a1 - cost, p1 + cost, p1_nodes, s2, a2, p2, p2_nodes, G); p1_nodes.pop_back(); } else if (u == s2 && testPairCompatible(s1, a1, v, a2 - cost, G)) { p2_nodes.push_back(v); listPairCompatible(s1, a1, p1, p1_nodes, v, a2 - cost, p2 + cost, p2_nodes, G); p2_nodes.pop_back(); } } } G.removed[u] = false; if ((u == s1 && p1 >= (beta + G.node_cost[s1])) || (u == s2 && p2 >= (beta + G.node_cost[s2]))) { vector adj_u; adj_u.swap(G.adj_list[u]); if ((u == s1 && dist_st(a2, s2, s1, G)) || (u == s2 && dist_st(a1, s1, s2, G))) listPairCompatible(s1, a1, p1, p1_nodes, s2, a2, p2, p2_nodes, G); G.adj_list[u].swap(adj_u); } } void bfs(WeightedDigraph &G, int source, set &in_sub) { queue Q; map dist; Q.push(source); dist[source] = 0; while (!Q.empty()) { int u = Q.front(); Q.pop(); in_sub.insert(u); int d = dist[u]; for (int i = 0; i < (int)G.adj_list[u].size(); i++) { int v = G.adj_list[u][i].node; if (!contains(in_sub, v)) { Q.push(v); dist[v] = d + 1; } } // We don't compress non-branching paths to an edge, but to a // vertex, this means that each non-branching vertex maybe // followed by a branching vertex. Implying that a path with // MAX_BRANCHES contains at most 2*MAX_BRANCHES+1 // vertices. However, we don't count the first and last vertices // (that are always branching), so the length is 2 * // (MAX_BRANCHES+1). if (d > 2*(MAX_BRANCHES+1)) return; } dist.clear(); } /* List all bubbles satisfying the path constraints. It does so using listPairCompatible function. */ void listAllBubbles(WeightedDigraph& G, int k_value, int UL_MAX, int LL_MAX, int LL_MIN) { //local a1 and a2 int a1 = LL_MAX - (k_value - 1), a2 = UL_MAX - (k_value - 1); beta = LL_MIN - (k_value - 1); for (int v = 0; v < (int)G.adj_list.size(); v++) { in_sub.clear(); bfs(G, v, in_sub); vector p1_nodes, p2_nodes; p1_nodes.push_back(v); p2_nodes.push_back(v); //checks if the user wants to output type0b or not if (OUTPUT_SNPS!=2) { //no, call normally listPairCompatible(v, a1, 0, p1_nodes, v, a2, 0, p2_nodes, G); }else { //yes. With this algorithm, maybe the only way to do this is to reexecute it passing a2 as LL_MIN and filtering the bubbles found s.t. lower path length > a1 listPairCompatible(v, a2, 0, p1_nodes, v, a2, 0, p2_nodes, G); } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// void printSummary( FILE* stream, int num_bubbles ) { fprintf( stream, "============================================================================\n" ); fprintf( stream, "Summary of results\n" ); fprintf( stream, "============================================================================\n" ); fprintf( stream, "No of bubbles: %d\n", num_bubbles ); fprintf( stream, "============================================================================\n" ); } void printUsageAndExit( char * name ) { fprintf( stderr, "Usage: %s infofile contents_file_edges contents_file_nodes basename_edges basename_nodes number_to_read k_value output_prefix edit_distance_threshold comment numbering_offset [-u UL_MAX] [-L LL_MAX] [-l LL_MIN] [-M MAX_BUBBLES] [-s] [-p] [-c] [-b] [-e MAX_MEMORY]\n", name ); fprintf( stderr, "\t [-u UL_MAX] Maximal length of the upper path of each bubble. Default: 1000000\n" ); fprintf( stderr, "\t [-L LL_MAX] Maximal length of the lower path of each bubble. Default: 2k-1\n" ); fprintf( stderr, "\t [-l LL_MIN] Minimal length of the lower path of each bubble. Default: 2k-8\n" ); fprintf( stderr, "\t [-M MAX_BUBBLES] Stop the process after this number of bubbles. Default: 10000\n" ); fprintf( stderr, "\t [-b MAX_BRANCHES] Maximum number of branches for each bubble. Default: 5\n"); fprintf( stderr, "\t [-v] Outputs the number of branching nodes in each path\n"); fprintf( stderr, "\t [-e MAX_MEMORY] Use an experimental algorithm that find bubbles by listing paths. You must provide the maximum size of the process's virtual memory (address space) in megabytes.\n"); exit( EXIT_FAILURE ); } //Path-enumeration algorithm to list bubbles - forward declaration void listAllBubblesUsingPath(WeightedDigraph& G); int main( int argc, char** argv ) { if ( argc < 12 ) printUsageAndExit( argv[0] ); vector label; vector allEdges; string output_prefix = argv[8]; k_value = atoi(argv[7]); seq_output_file_type0 = fopen( string(output_prefix + "_type0.fa").c_str(), "w" ); seq_output_file_type1234 = fopen( string(output_prefix + "_type1234.fa").c_str(), "w" ); MIN_DIST = atoi(argv[9]); comment = argv[10]; BUBBLE_COUNT_OFFSET = atoi(argv[11]); LL_MAX = 2 * k_value - 1; LL_MIN = 2 * k_value - 10; UL_MAX = 1000000; MAX_BUBBLES = 10000; MAX_BRANCHES = 5; OUTPUT_CONTEXT = false; OUTPUT_SNPS = 0; OUTPUT_PATH = false; OUTPUT_BRANCH = false; EXPERIMENTAL_ALG = false; MAX_MEMORY = 0; int required_sequence = atoi( argv[6] ); int temoin; while ( (temoin = getopt ( argc-11, &argv[11], "u:L:l:M:b:e:s:cpv" )) != -1 ) { switch ( temoin ) { case 'u' : { UL_MAX = atoi( optarg ); break; } case 'L' : { LL_MAX = atoi( optarg ); break; } case 'l' : { LL_MIN = atoi( optarg ); break; } case 'M': { MAX_BUBBLES = atoi( optarg ); break; } case 'b': { MAX_BRANCHES = atoi( optarg ); break; } case 's': { OUTPUT_SNPS = atoi( optarg ); switch (OUTPUT_SNPS) { case 0: fprintf(stderr, "Will not output SNPs and sequencing errors\n" ); break; case 1: fprintf(stderr, "Will output Type0a-SNPs\n" ); break; case 2: fprintf(stderr, "Will output Type0a and Type0b SNPs\n" ); break; } break; } case 'c': { OUTPUT_CONTEXT = true; fprintf(stderr, "Will output bubble contexts!\n"); break; } case 'p': { OUTPUT_PATH = true; path_output_file = fopen( string(output_prefix + ".path").c_str(), "w" ); break; } case 'v': { OUTPUT_BRANCH = true; break; } case 'e': { EXPERIMENTAL_ALG = true; if (strcmp(optarg, "unlimited") == 0) MAX_MEMORY = -1; else MAX_MEMORY = atoi( optarg ); break; } default: { printUsageAndExit( argv[0] ); } } } fprintf(stdout, "\t Enumerating bubbles with at most %d branching nodes in each path!\n", MAX_BRANCHES); if (EXPERIMENTAL_ALG) { fprintf(stdout, "\t Using the experimental algorithm with maximum memory = "); if (MAX_MEMORY==-1) printf("unlimited.\n"); else printf("%d MB.\n", MAX_MEMORY); struct rlimit vmLimit; vmLimit.rlim_cur = vmLimit.rlim_max = (MAX_MEMORY==-1 ? RLIM_INFINITY : MAX_MEMORY * 1024 * 1024); setrlimit(RLIMIT_AS, &vmLimit); } EdgeLoader edgeloader(allEdges); NodeLoader nodeloader(seqs, label); bool atleast4nodes = read_edges_and_nodes_withoptimIO (argv[1],argv[2],argv[3],argv[4],argv[5],&required_sequence, edgeloader, nodeloader); if (atleast4nodes){ nb_nodes = (int)label.size(); map label_to_node; // Set the label mapping, necessary only to read the edges for (int i = 0; i < nb_nodes; i++) label_to_node[label[i]] = i; // This is a edge-weighted directed graph. The forward and reverse // node in the bidirected de Bruijn graph are split in two nodes. WeightedDigraph G(2 * nb_nodes); for (int i = 0; i < (int)allEdges.size(); i++) { int u = label_to_node[allEdges[i].getFirst()] + (allEdges[i].label[0] == 'F' ? 0 : nb_nodes); int v = label_to_node[allEdges[i].getSecond()] + (allEdges[i].label[1] == 'F' ? 0 : nb_nodes); int cost = strlen(seqs[ label_to_node[allEdges[i].getSecond()] ]) - (k_value - 1); G.adj_list[u].push_back(WeightedEdge(v, cost)); } // We add weights for the nodes only for convenience. All algorithms // work under the assumption of a edge-weighted directed graph. for (int i = 0; i < (int)G.adj_list.size(); i++) G.node_cost[i] = strlen(seqs[i % nb_nodes]) - (k_value - 1); a1Global = LL_MAX - (k_value - 1); a2Global = UL_MAX - (k_value - 1); beta = LL_MIN - (k_value - 1); if (!EXPERIMENTAL_ALG) { listAllBubbles(G, k_value, UL_MAX, LL_MAX, LL_MIN); } else { listAllBubblesUsingPath(G); } } printSummary(stdout, nbBubbles); for (unsigned int i=0; i allPaths; //will contain all paths from s to any t with bounded length == a2 with at most b branching vertices vector explored; //will keep track of the nodes that were already explored on currentPath Path currentPath; ////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// //helper functions //check if a node is branching or not int isBranching (int node, WeightedDigraph& G) { return (G.outDegree(node) != 1 || inDegree(node, G) != 1) ? 1 : 0; } //check if both paths are vertex-disjoint bool vertexDisjoint (const vector &p1, const vector &p2) { for (int i=1;i<(int)p1.size()-1;i++) { if (find(p2.begin(), p2.end(), p1[i]) != p2.end()) return false; } return true; } //get the number of branching nodes if a new vertex is added to this path int getNbBranchingNodesIfAddAVertex(const Path &path, WeightedDigraph& G) { if (path.nodes.size() <= 1) return 0; return path.branchingNodes + isBranching(path.nodes.back(), G); } ////////////////////////////////////////////////////////////////////////////////////////////// //Recursive function that finds all simple paths from a source s such that the path respects MAX_BRANCHES, beta and a2 void DFSEnum(WeightedDigraph& G, int s) { //add s to the path explored[s]=true; //s is in the current Path //add this path to allPaths if (currentPath.distance >= beta) allPaths.push_back(currentPath); for (int e = 0; e < (int)G.adj_list[s].size(); e++) { //iterates over all edges of s int v = G.adj_list[s][e].node; //check if v should be explored int cost = G.node_cost[s]; int newNbBranchingNodes = getNbBranchingNodesIfAddAVertex(currentPath, G); int newCost = (currentPath.nodes.size() > 1 ? currentPath.distance + cost : 0); if (!explored[v] && //if v is not already in the path newNbBranchingNodes <= MAX_BRANCHES && newCost <= a2Global) { //yes, v should be explored //Configure the currentPath accordingly int oldDistance = currentPath.distance; currentPath.distance = newCost; int oldBranchingNodes = currentPath.branchingNodes; currentPath.branchingNodes = newNbBranchingNodes; currentPath.nodes.push_back(v); //call DFS DFSEnum(G, v); //DesConfigure the currentPath accordingly currentPath.distance = oldDistance; currentPath.branchingNodes = oldBranchingNodes; currentPath.nodes.pop_back(); } } //already explored all paths with this prefix explored[s]=false; //s is not in the currentPath } //list all bubbles by listing all simple paths void findAllBubblesUsingSimpleQueueDFS(WeightedDigraph& G, int source) { //initialize the global variables allPaths.clear(); fill(explored.begin(), explored.end(), false); currentPath.branchingNodes=0; currentPath.distance=0; currentPath.nodes.clear(); //find all paths currentPath.nodes.push_back(source); DFSEnum(G, source); //now list all bubbles map > targetToDistPath; for (vector::iterator it = allPaths.begin(); it != allPaths.end(); ++it) targetToDistPath[it->nodes.back()].push_back(&(*it)); for (map >::iterator targetToDistPathIt = targetToDistPath.begin(); targetToDistPathIt != targetToDistPath.end(); ++targetToDistPathIt) { vector allSTPaths = targetToDistPathIt->second; for (vector::iterator i = allSTPaths.begin(); i != allSTPaths.end(); ++i) { vector::iterator j = i; for (++j; j != allSTPaths.end(); ++j) { Path* lowerPath; Path* upperPath; if ( (*i)->distance < (*j)->distance) { lowerPath = *i; upperPath = *j; } else { lowerPath = *j; upperPath = *i; } if ( (lowerPath->distance <= a1Global || (OUTPUT_SNPS==2 && lowerPath->distance==upperPath->distance) ) && //if we respect LL_MAX (this is for type 0a, 1, 2 and 3) OR we should output all Type0b and both paths have the same length (this is only for type0b) (upperPath->nodes.size() >= 3 || lowerPath->nodes.size() >= 3) && vertexDisjoint(lowerPath->nodes, upperPath->nodes)) { processBubbleFound(upperPath->nodes, lowerPath->nodes, G); } } } } } void listAllBubblesUsingPath(WeightedDigraph& G) { explored = vector(G.adj_list.size(), false); //list all bubbles starting with each node v for (int v = 0; v < (int) G.adj_list.size(); v++) { if (G.outDegree(v) >= 2) //pruning findAllBubblesUsingSimpleQueueDFS(G, v); } } kissplice-2.4.0-p1/modules/BubbleEnumeration.h000644 000765 000024 00000012456 12575016362 021362 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #include #include #include #include #include #include #include #include "NGraph.h" #include "Utils.h" #ifndef MOUTH_ENUMERATION_H #define MOUTH_ENUMERATION_H template bool read_edges_and_nodes_withoptimIO(char* filename_info, char* filename_contents_edge, char* filename_contents_node, char* filename_edge, char* filename_node, int *required_sequence, TEdgeFunctor edgefunctor, TNodeFunctor& nodefunctor) { //////////////////////////// //////////////////////////// //////////////////////////// // IO optimization (start) //////////////////////////// int bcc_size, records_per_file, number_of_files_max, file_index; FILE *info_file = fopen(filename_info, "r"); if (info_file == NULL) { fprintf(stderr, "Problem opening %s!\n", filename_info); exit(0); } FILE *contents_file_edge = fopen(filename_contents_edge, "r"); if (contents_file_edge == NULL) { fprintf(stderr, "Problem opening %s!\n", filename_contents_edge); exit(0); } FILE *contents_file_node = fopen(filename_contents_node, "r"); if (contents_file_node == NULL) { fprintf(stderr, "Problem opening %s!\n", filename_contents_node); exit(0); } // read the info file fscanf(info_file, "%d \n",&bcc_size ); fscanf(info_file, "%d \n",&records_per_file ); // find in which file is the required record number_of_files_max = NUMBEROFFILES; if (bcc_sizenumber_of_files_max) file_index = number_of_files_max; if ( (*required_sequence <=0) || (*required_sequence > bcc_size) ) { fprintf(stderr, "Problem opening sequence %d in edge/node files !\n", *required_sequence); exit(0); } // filenames char total_edge_fname[1024]; sprintf( total_edge_fname, "%s_%d",filename_edge,file_index ); char total_node_fname[1024]; sprintf( total_node_fname, "%s_%d",filename_node,file_index ); //////////////////////////// // IO optimization (end) //////////////////////////// //////////////////////////// //////////////////////////// FILE* edge_file = open_file(total_edge_fname); FILE* node_file = open_file(total_node_fname); bool atleast4nodes = read_node_noncontigous_file_withoptimIO( contents_file_node, node_file, required_sequence, &file_index, nodefunctor); if (atleast4nodes){ read_edge_file_withoptimIO( contents_file_edge, edge_file, required_sequence, &file_index, edgefunctor ); } fclose( contents_file_edge ); fclose( contents_file_node ); fclose( edge_file ); fclose( node_file ); return atleast4nodes; } //Type used to represent a path. //It keeps track of the length (distance) of the path, its number of branching nodes and its nodes typedef struct Path { int distance; int branchingNodes; vector nodes; Path():distance(0), branchingNodes(0), nodes(){} Path (int distance, int branchingNodes, const vector &nodes) : distance(distance), branchingNodes(branchingNodes), nodes(nodes){} }; #endif /* MOUTH_ENUMERATION_H */ kissplice-2.4.0-p1/modules/CEdge.cpp000644 000765 000024 00000012127 12575016362 017255 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ // =========================================================================== // Include Libraries // =========================================================================== // =========================================================================== // Include Project Files // =========================================================================== #include "CEdge.h" // =========================================================================== // Declare Used Namespaces // =========================================================================== //############################################################################ // # // Class CEdge # // # //############################################################################ // =========================================================================== // Static attributes // =========================================================================== // =========================================================================== // Constructors // =========================================================================== CEdge::CEdge( void ) : _pair() { } CEdge::CEdge( const CEdge& model ) : _pair( model._pair ) { } CEdge::CEdge( int f, int s ) : _pair( f, s ) { } // =========================================================================== // Destructor // =========================================================================== // =========================================================================== // Operators // =========================================================================== bool CEdge::operator==( const CEdge& that ) const { return _pair == that._pair; } bool CEdge::operator!=( const CEdge& that ) const { return _pair != that._pair; } bool CEdge::operator<( const CEdge& that ) const { return _pair < that._pair; } // =========================================================================== // Public Methods // =========================================================================== CEdge& CEdge::swap_ends( void ) { swap( _pair.first, _pair.second ); return *this; } // =========================================================================== // Protected Methods // =========================================================================== // =========================================================================== // Non inline accessors // =========================================================================== kissplice-2.4.0-p1/modules/CEdge.h000644 000765 000024 00000015663 12575016362 016732 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #ifndef EDGE_H #define EDGE_H // =========================================================================== // Include Libraries // =========================================================================== #include #include #include #include // =========================================================================== // Include Project Files // =========================================================================== // =========================================================================== // Class declarations // =========================================================================== // =========================================================================== // Declare Used Namespaces // =========================================================================== using namespace std; //! Edge class /*! * \brief Class meant to be used by CGraph (compact graph) to represent edges. * * It encapsulates a pair of int (u -> v edge), each int (u or v) being a node id */ class CEdge { public : // ======================================================================= // Enums // ======================================================================= // ======================================================================= // Constructors // ======================================================================= CEdge( void ); CEdge( const CEdge& model ); CEdge( int f, int s ); // ======================================================================= // Destructor // ======================================================================= // ======================================================================= // Accessors: getters // ======================================================================= inline int getFirst( void ); inline int getSecond( void ); // ======================================================================= // Accessors: setters // ======================================================================= // ======================================================================= // Operators // ======================================================================= bool operator==( const CEdge& that ) const; bool operator!=( const CEdge& that ) const; bool operator<( const CEdge& that ) const; // ======================================================================= // Public Methods // ======================================================================= CEdge& swap_ends( void ); // ======================================================================= // Public Attributes // ======================================================================= protected : pair _pair; // ======================================================================= // Forbidden Constructors // ======================================================================= // ======================================================================= // Protected Methods // ======================================================================= // ======================================================================= // Protected Attributes // ======================================================================= }; // =========================================================================== // Getters' definitions // =========================================================================== inline int CEdge::getFirst( void ) { return _pair.first; } inline int CEdge::getSecond( void ) { return _pair.second; } // =========================================================================== // Setters' definitions // =========================================================================== // =========================================================================== // Inline Operators' definitions // =========================================================================== // =========================================================================== // Inline functions' definition // =========================================================================== #endif // EDGE_H kissplice-2.4.0-p1/modules/CGraph.cpp000644 000765 000024 00000016402 12575016362 017452 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ // =========================================================================== // Include Libraries // =========================================================================== #include #include // =========================================================================== // Include Project Files // =========================================================================== #include // =========================================================================== // Declare Used Namespaces // =========================================================================== using namespace std; //############################################################################ // # // Class CGraph # // # //############################################################################ // =========================================================================== // Constructors // =========================================================================== CGraph::CGraph(int nbNodes, vector& allEdges, int kValue) { k_value = kValue; adj_list.reserve(nbNodes); adj_list_sz.reserve(nbNodes); int j = 0; int offset = 0; for ( int i = 0 ; i < nbNodes ; i++ ) { // Count the number of edges (they are ordered in all_edges) of node i j = 0; while ( offset + j < (int) allEdges.size() && allEdges[offset + j].getFirst() == i ) { j++; } // ... adj_list.push_back(j != 0 ? new int[j] : NULL); adj_list_sz.push_back(0); offset += j; } // ... for ( int i = 0 ; i < (int)allEdges.size() ; i++ ) { insert_unique_edge_dir( allEdges[i].getFirst(), allEdges[i].getSecond() ); } } // =========================================================================== // Destructors // =========================================================================== CGraph::~CGraph(void) { } // =========================================================================== // Public Methods // =========================================================================== void CGraph::insert_unique_edge_dir(int u, int v) { int i; int* neighbours = adj_list[u]; int nb_neighbours = adj_list_sz[u]; // Find corresponding neighbour (if it exists) for ( i = 0 ; i < nb_neighbours ; i++, neighbours++ ) { if ( *neighbours == v ) { break; } } // If not found, add the missing edge if ( i == nb_neighbours ) { *neighbours = v; adj_list_sz[u]++; } } void CGraph::insert_unique_edge( int u, int v ) { insert_unique_edge_dir(u,v); insert_unique_edge_dir(v,u); } void CGraph::destroy_adj_list(void) { for (int i = 0; i < (int)adj_list.size(); i++) delete[] adj_list[i]; vector().swap(adj_list); } void CGraph::destroy(void) { destroy_adj_list(); vector().swap(adj_list_sz); } //================================================================ // Reading functions //================================================================ int count_nb_lines( FILE* file ) { int ch, number_of_lines = 0; while (EOF != (ch=getc(file))) if ('\n' == ch) number_of_lines++; // Set the cursor back to the begining of the file. rewind(file); // Don't care if the last line has a '\n' or not. We over-estimate it. return number_of_lines + 1; } void read_node_file( FILE* node_file, vector& seqs, int k_val ) { char* buffer = new char[100 * MAX]; char* seq; seqs.reserve(count_nb_lines(node_file)); while ( fgets(buffer, 100 * MAX, node_file) != NULL ) { char* p; if (strlen(buffer) == 100 * MAX) { p = strtok(buffer, "\t\n"); fprintf(stdout, "ERROR: node %s with sequence larger than %d!", p, 100 * MAX); exit(0); } // Node label p = strtok( buffer, "\t\n" ); // Node seq p = strtok( NULL, "\t\n" ); seq = new char[strlen(p) + 1]; strcpy( seq, p ); seqs.push_back( seq ); //~ printf( "pushed %s (0x%x)|\n", seq, seq ); //~ getchar(); //~ delete [] seq; } delete [] buffer; } void read_edge_file( FILE *edge_file, vector& edges ) { char* buffer = new char[100 * MAX]; char* u = new char[MAX]; char* v = new char[MAX]; char* label = new char[MAX]; edges.reserve(count_nb_lines(edge_file)); while ( fgets(buffer, 100 * MAX, edge_file) != NULL ) { char* p; // outgoing node p = strtok( buffer, "\t\n" ); strcpy( u, p ); // incoming node p = strtok( NULL, "\t\n" ); strcpy( v, p ); // edge label p = strtok( NULL, "\t\n" ); strcpy(label, p); edges.push_back( LabelledCEdge( atoi(u), atoi(v), label ) ); } sort( edges.begin(), edges.end() ); delete [] buffer; delete [] u; delete [] v; delete [] label; } kissplice-2.4.0-p1/modules/CGraph.h000644 000765 000024 00000030075 12575016362 017121 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #ifndef COMPRESSED_GRAPH_H #define COMPRESSED_GRAPH_H // =========================================================================== // Include Libraries // =========================================================================== #include #include #include #include #include #include // =========================================================================== // Include Project Files // =========================================================================== #include "CEdge.h" #include "LabelledCEdge.h" // =========================================================================== // Class declarations // =========================================================================== // =========================================================================== // Declare Used Namespaces // =========================================================================== using namespace std; //! A class representing the compacted De-Bruijn graphs /*!\ * \brief Its light structure is used for the detection of bi-connected * component in the graph. */ #define MAX 1024 class CGraph { public: // ======================================================================= // Constructors // ======================================================================= /*! * \brief CGraph constructor. * Initializes the attributes according to the node numbers * Creates directly the CGraph object with its edges from allEdges */ CGraph( int nbNodes, vector & allEdges, int kValue ); // ======================================================================= // Destructors // ======================================================================= ~CGraph(void); // ======================================================================= // Public Attributes // ======================================================================= int k_value; // ======================================================================= // Public Methods // ======================================================================= inline int *get_adj_list(int node) const; inline int get_adj_list_sz(int node) const; inline int get_n_nodes(void) const; void insert_unique_edge_dir(int u, int v); void insert_unique_edge(int u, int v); void destroy_adj_list(void); void destroy(void); protected: // ======================================================================= // Forbidden Constructors // ======================================================================= CGraph(const CGraph &model) { printf("%s:%d: error: call to forbidden constructor of CGraph.\n", __FILE__, __LINE__); exit(EXIT_FAILURE); }; CGraph( void ) { printf("%s:%d: error: call to forbidden constructor of CGraph.\n", __FILE__, __LINE__); exit(EXIT_FAILURE); }; // ======================================================================= // Protected Methods // ======================================================================= // ======================================================================= // Protected Attributes // ======================================================================= // NOTA: The neighborhood size is always <= 8 //! Adjacency list /*! * adj_list is a vector of int tables containing the IDs of "outgoing neighbours" * i.e. neighbours v having a u -> v edge (u being the present node). * adj_list_sz contains the corresponding sizes */ vector adj_list; vector adj_list_sz; }; // =========================================================================== // Inline functions' definition // =========================================================================== int *CGraph::get_adj_list(int node) const { return adj_list[node]; } int CGraph::get_adj_list_sz(int node) const { return adj_list_sz[node]; } int CGraph::get_n_nodes(void) const { return (int) adj_list.size(); } // =========================================================================== // Other functions // =========================================================================== void read_node_file(FILE *node_file, vector& seqs, int k_val); void read_edge_file(FILE *edge_file, vector& edges); // Functor that load the bcc nodes in memory class NodeLoader { private: vector& _seqs; vector& _label; public: NodeLoader(vector& seqs, vector& label) : _seqs(seqs), _label(label) {} void operator()(FILE* node_file, int written_lines){ char* buffer = new char[100 * MAX]; for (int i = 0; i < written_lines ; i++) { char* p; fgets(buffer, 100 * MAX, node_file); if (strlen(buffer) == 100 * MAX) { p = strtok(buffer, "\t\n"); fprintf(stdout, "ERROR: node %s with sequence larger than %d!", p, 100 * MAX); exit(0); } // Node label p = strtok( buffer, "\t\n" ); _label.push_back(atoi(p)); // Node seq p = strtok( NULL, "\t\n" ); char* seq = new char[strlen(p) + 1]; strcpy( seq, p ); // Node seq_r p = strtok( NULL, "\t\n" ); _seqs.push_back( seq ); } delete [] buffer; } }; // return false if nbnodes<4 // required a functor to acts on the file lines template bool read_node_noncontigous_file_withoptimIO( FILE *contents_file_node, FILE* node_file, int *required_sequence, int *file_index, TFunctor& functor ) { char* buffer = new char[100 * MAX]; //////////////////////////// //////////////////////////// //////////////////////////// // IO optimization (start) //////////////////////////// int written_lines_before, written_lines_for_record, written_lines; //normally would skip directly to the record, but for the time being use the formatted file for (int i = 0; i < *required_sequence+*file_index-2 ; i++) fgets(buffer, 100 * MAX, contents_file_node); fscanf(contents_file_node, "%d \n",&written_lines_before ); fscanf(contents_file_node, "%d \n",&written_lines_for_record ); // if a graph node was not written to disk if ( written_lines_before == written_lines_for_record) { fprintf(stderr, "The required record %d does not exist in the bcc graph !\n", *required_sequence); exit(0); } // where to search the record written_lines = written_lines_for_record - written_lines_before; bool atleast4nodes = false; if (written_lines>=4){ //Less than 4 nodes, cannot contain a bubble! atleast4nodes = true; //normally would skip directly to the record, but for the time being use the formatted file for (int i = 0; i < written_lines_before ; i++) fgets(buffer, 100 * MAX, node_file); //////////////////////////// // IO optimization (end) //////////////////////////// //////////////////////////// //////////////////////////// functor(node_file, written_lines); } delete [] buffer; return atleast4nodes; } // Functor that load the bcc edges in memory class EdgeLoader { private: vector& _edges; public: EdgeLoader(vector& edges) : _edges(edges) {} void operator()(FILE* edge_file, int written_lines){ if (written_lines>0){ char* buffer = new char[100 * MAX]; char* u = new char[MAX]; char* v = new char[MAX]; char* label = new char[MAX]; _edges.reserve(written_lines);//count_nb_lines(edge_file)); for (int i = 0; i < written_lines ; i++) { char* p; fgets(buffer, 100 * MAX, edge_file); // outgoing node p = strtok( buffer, "\t\n" ); strcpy( u, p ); // incoming node p = strtok( NULL, "\t\n" ); strcpy( v, p ); // edge label p = strtok( NULL, "\t\n" ); strcpy(label, p); _edges.push_back( LabelledCEdge( atoi(u), atoi(v), label ) ); } sort( _edges.begin(), _edges.end() ); delete [] u; delete [] v; delete [] label; delete [] buffer; } } }; // required a functor to acts on the file lines template void read_edge_file_withoptimIO( FILE *contents_file_edge, FILE *edge_file, int *required_sequence, int *file_index, TFunctor& functor ) { char* buffer = new char[100 * MAX]; //////////////////////////// //////////////////////////// //////////////////////////// // IO optimization (start) // [replicate as above] //////////////////////////// int written_lines_before, written_lines_for_record, written_lines; //normally would skip directly to the record, but for the time being use the formatted file for (int i = 0; i < *required_sequence+*file_index-2 ; i++) fgets(buffer, 100 * MAX, contents_file_edge); fscanf(contents_file_edge, "%d \n",&written_lines_before ); fscanf(contents_file_edge, "%d \n",&written_lines_for_record ); // if a graph edge was not written to disk if ( written_lines_before == written_lines_for_record) { fprintf(stderr, "The required record %d does not exist in the bcc graph !\n", *required_sequence); exit(0); } // where to search the record written_lines = written_lines_for_record - written_lines_before; //normally would skip directly to the record, but for the time being use the formatted file for (int i = 0; i < written_lines_before ; i++) fgets(buffer, 100 * MAX, edge_file); //////////////////////////// // IO optimization (end) //////////////////////////// //////////////////////////// //////////////////////////// functor(edge_file, written_lines); delete [] buffer; } #endif // COMPRESSED_GRAPH_H kissplice-2.4.0-p1/modules/check_error_removal.cpp000644 000765 000024 00000017342 12575016362 022325 0ustar00ishistaff000000 000000 #include #include #include #include #include #include "LabelledCEdge.h" #define MAX 1024 using namespace std; struct NodeSeq { char *seq; int node; NodeSeq(char *initSeq, int initNode) : seq(initSeq), node(initNode) { } bool operator== (const NodeSeq& rhs) const { return (strcmp(seq, rhs.seq) == 0); } bool operator< (const NodeSeq& rhs) const { return (strcmp(seq, rhs.seq) < 0); } }; int count_nb_lines( FILE* file ) { int ch, number_of_lines = 0; while (EOF != (ch=getc(file))) if ('\n' == ch) number_of_lines++; // Set the cursor back to the begining of the file. rewind(file); // Don't care if the last line has a '\n' or not. We over-estimate it. return number_of_lines + 1; } FILE* open_file( char* filename ) { FILE* file = fopen( filename, "r" ); if ( file == NULL ) { fprintf( stderr, "Problem opening %s!\n", filename ); exit( EXIT_FAILURE ); } return file; } static char complement(char b) { switch(b) { case 'A': return 'T'; case 'T': return 'A'; case 'G': return 'C'; case 'C': return 'G'; case 'a': return 't'; case 't': return 'a'; case 'g': return 'c'; case 'c': return 'g'; case 'N': return 'N'; case '*': return '*'; } return '?'; } string reverse_complement(string seq) { string s(seq.begin(),seq.end()); string::iterator pos; for (pos = s.begin(); pos != s.end(); ++pos) { // cout << *pos; } // cout << endl; reverse(s.begin(), s.end()); for(pos=s.begin();pos!=s.end();++pos) *pos=complement(*pos); return s; } void readNodeFile(char* nodes_fname, vector& nodesF, vector& nodesR, int k_val ) { FILE* node_file = open_file(nodes_fname); char* buffer = new char[100 * MAX]; char* seq; nodesF.reserve(count_nb_lines(node_file)); nodesR.reserve(count_nb_lines(node_file)); while ( fgets(buffer, 100 * MAX, node_file) != NULL ) { char* p; if (strlen(buffer) == 100 * MAX) { p = strtok(buffer, "\t\n"); fprintf(stdout, "ERROR: node %s with sequence larger than %d!", p, 100 * MAX); exit(0); } // Node label, should be contiguous p = strtok( buffer, "\t\n" ); int node = atoi(p); // Node seq p = strtok( NULL, "\t\n" ); // We need the prefix of size k seq = new char[k_val + 1]; strncpy( seq, p, k_val); seq[k_val] = '\0'; nodesF.push_back( NodeSeq(seq, node) ); // And the prefix of size k in the reverse complement seq = new char[k_val + 1]; string rev_comp = reverse_complement(string(p)); strncpy( seq, rev_comp.c_str(), k_val); seq[k_val] = '\0'; nodesR.push_back( NodeSeq(seq, node) ); //printf("%d %s %s\n", node, p, rev_comp.c_str()); } delete [] buffer; fclose(node_file); } char *rev_seq = NULL; char *revComp(char *seq) { int size = strlen(seq); if (rev_seq == NULL) rev_seq = new char[size + 1]; for (int i = size - 1; i >= 0; i--) rev_seq[(size - 1) - i] = complement(seq[i]); rev_seq[size] = '\0'; return rev_seq; } int findNode(char *query, vector& nodes) { vector::iterator low, low_r; char *query_r = revComp(query); // The value of "x" in NodeSeq(query, x) doesn't matter low = lower_bound(nodes.begin(), nodes.end(), NodeSeq(query, 0)); low_r = lower_bound(nodes.begin(), nodes.end(), NodeSeq(query_r, 0)); if (low != nodes.end() && *low == NodeSeq(query, 0)) return low->node; if (low_r != nodes.end() && *low_r == NodeSeq(query_r, 0)) return low_r->node; return -1; } void readCounts(char* counts_fname, map& counts) { FILE *count_file = open_file(counts_fname); char* buffer = new char[100 * MAX]; while ( fgets(buffer, 100 * MAX, count_file) != NULL ) { char* p; // Sequence p = strtok( buffer, "\t\n " ); string kmer = p; // Count p = strtok( NULL, "\t\n " ); counts[kmer] = atoi(p); } delete [] buffer; fclose(count_file); } int kcounts(string query, map counts) { if (counts.find(query) != counts.end()) return counts[query]; if (counts.find(reverse_complement(query)) != counts.end()) return counts[reverse_complement(query)]; // fprintf(stderr, "k-mer not found!\n"); return 0; } char ACTG[4] = {'A','C','T','G'}; void readEdgeFile( char* nodes_fname, vector& edges ) { FILE *edge_file = open_file(nodes_fname); char* buffer = new char[100 * MAX]; char* u = new char[MAX]; char* v = new char[MAX]; char* label = new char[MAX]; edges.reserve(count_nb_lines(edge_file)); while ( fgets(buffer, 100 * MAX, edge_file) != NULL ) { char* p; // outgoing node p = strtok( buffer, "\t\n" ); strcpy( u, p ); // incoming node p = strtok( NULL, "\t\n" ); strcpy( v, p ); // edge label p = strtok( NULL, "\t\n" ); strcpy(label, p); edges.push_back( LabelledCEdge( atoi(u), atoi(v), label ) ); } sort( edges.begin(), edges.end() ); delete [] buffer; delete [] u; delete [] v; delete [] label; fclose(edge_file); } LabelledCEdge reverse(LabelledCEdge e) { char *label = new char[3]; label[0] = e.label[1] == 'F' ? 'R' : 'F'; label[1] = e.label[0] == 'F' ? 'R' : 'F'; label[2] = '\0'; return LabelledCEdge(e.getSecond(), e.getFirst(), label); } int findEdge(vector& allEdges, LabelledCEdge e) { vector::iterator low; low = lower_bound(allEdges.begin(), allEdges.end(), e); if (low != allEdges.end()) return low - allEdges.begin(); fprintf(stderr, "inconsistent graph!"); return -1; } int main( int argc, char** argv ) { if ( argc < 6 ) { fprintf( stderr, "Wrong number of arguments!\n" ); fprintf( stderr, "Usage: ./check_error_removal removed.edges nodes_file k_value k_mer_count_file cutoff\n" ); return 0; } int k_value = atoi( argv[3] ); double cutoff = atof( argv[5] ); // Read node file vector nodesF, nodesR; readNodeFile(argv[2], nodesF, nodesR, k_value ); // Read count file map counts; readCounts(argv[4], counts); // printf("\n"); // vector::iterator it; // for (it = nodesF.begin(); it != nodesF.end(); it++) // printf("%d %s %d\n", it->node, it->seq, kcounts(string(it->seq), counts)); // printf("\n"); // for (it = nodesR.begin(); it != nodesR.end(); it++) // printf("%d %s %d\n", it->node, it->seq, kcounts(string(it->seq), counts)); vector allEdges; readEdgeFile( argv[1], allEdges ); vector verified(allEdges.size(), false); for (int i = 0; i < (int)allEdges.size(); i++) if (!verified[i]) { int u = allEdges[i].getFirst(); int v = allEdges[i].getSecond(); string label = allEdges[i].label; string kmer, pref; if (label[1] == 'F') kmer = nodesF[v].seq; else kmer = nodesR[v].seq; pref = kmer.substr(0,k_value -1); int sum = 0; for (int j = 0; j < 4; j++) sum += kcounts(string(pref+ACTG[j]), counts); double ratio = (double)kcounts(kmer, counts) / (double)sum; //fprintf(stderr, "edge %d -> %d (%s) ratio = %lf\n", u, v, label.c_str(), ratio); if (ratio < cutoff) { verified[i] = true; verified[findEdge(allEdges, reverse(allEdges[i]))] = true; } } bool flag = true; for (int i = 0; i < (int)allEdges.size(); i++) if (!verified[i]) { int u = allEdges[i].getFirst(); int v = allEdges[i].getSecond(); string label = allEdges[i].label; fprintf(stderr, "edge %d -> %d (%s) should not be removed\n", u, v, label.c_str()); flag = false; } if (flag) fprintf(stderr, "Removed edges are correct!\n"); return 0; } kissplice-2.4.0-p1/modules/CleanDuplicates.cpp000644 000765 000024 00000010002 12575016362 021334 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #include #include #include #include #include #include "Utils.h" #include "NGraph.h" using namespace std; struct block { string l[4]; }; bool comp_block(block a, block b) { string a_up = a.l[1]; string a_low = a.l[3]; string b_up = b.l[1]; string b_low = b.l[3]; // upper path is the lexicograficaly smaller, canonical form. if (a_low < a_up) swap(a_low, a_up); if (b_low < b_up) swap(b_low, b_up); return (a_up < b_up) || (a_up == b_up && a_low < b_low); } bool eq(block a, block b) { string a_up = a.l[1], a_low = a.l[3], b_up = b.l[1], b_low = b.l[3]; // upper path is the lexicograficaly smaller, canonical form. if (a_low < a_up) swap(a_low, a_up); if (b_low < b_up) swap(b_low, b_up); return a_up == b_up && a_low == b_low; } block reverse_complement(block &b) { block res; res.l[1] = reverse_complement(b.l[1]); res.l[3] = reverse_complement(b.l[3]); res.l[0] = b.l[0]; res.l[2] = b.l[2]; return res; } int main(int argc, char **argv) { ifstream bubbles_file; bubbles_file.open(argv[1]); string line; int nlines = 0; block cur; vector all_blocks; while (bubbles_file.good()) { getline(bubbles_file, line); nlines++; cur.l[(nlines-1) % 4] = line; if (nlines % 4 == 0) { // convert the sequences to a canonical form, lexicografically smaller (forward and reverse) if (comp_block(reverse_complement(cur), cur)) cur = reverse_complement(cur); all_blocks.push_back(cur); } } sort(all_blocks.begin(), all_blocks.end(), comp_block); vector::iterator end = unique(all_blocks.begin(), all_blocks.end(), eq); vector::iterator it; for (it = all_blocks.begin(); it != end; it++) for (int j = 0; j < 4; j++) cout << it->l[j] << "\n"; bubbles_file.close(); return 0; } kissplice-2.4.0-p1/modules/ClusterBubbles.cpp000644 000765 000024 00000014132 12575016362 021224 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include #include using namespace std; #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MIN3(a,b,c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) struct block { string l[4], bcc; double rank; }; static char complement(char b) { switch(b) { case 'A': return 'T'; case 'T': return 'A'; case 'G': return 'C'; case 'C': return 'G'; case 'a': return 't'; case 't': return 'a'; case 'g': return 'c'; case 'c': return 'g'; case 'N': return 'N'; case '*': return '*'; } return '?'; } string reverse_complement(string seq) { string s(seq.begin(),seq.end()); string::iterator pos; for (pos = s.begin(); pos != s.end(); ++pos) { // cout << *pos; } // cout << endl; reverse(s.begin(), s.end()); for(pos=s.begin();pos!=s.end();++pos) *pos=complement(*pos); return s; } bool comp_lower(block a, block b) { return a.l[3] < b.l[3]; } bool comp_bcc(block a, block b) { return a.bcc < b.bcc; } block reverse_complement(block &b) { block res; res.l[1] = reverse_complement(b.l[1]); res.l[3] = reverse_complement(b.l[3]); res.l[0] = b.l[0]; res.l[2] = b.l[2]; return res; } string extract_bcc(block &b) { int sep = b.l[0].find_first_of("|"); return b.l[0].substr(0, sep); } double extract_rank(block &b) { int sep = b.l[0].find_last_of("|"); string rank = b.l[0].substr(sep+1); sep = rank.find_first_of("_"); float res = atof(rank.substr(sep+1).c_str()); /* trick to test is res = nan */ if (res != res) res = 0; return res; } void print_block(FILE *stream, block &b) { for (int i = 0; i < 4; i++) fprintf(stream, "%s\n", b.l[i].c_str()); } map > adj; vector all_blocks; int max_rank, max_ind; void dfs(int node, map &visited) { visited[node] = true; if (all_blocks[node].rank >= max_rank) { max_rank = all_blocks[node].rank; max_ind = node; } for (int i = 0; i < (int)adj[node].size(); i++) { int v = adj[node][i]; if (!visited[v]) dfs(v, visited); } } int nprinted = 0; void print_clusters(FILE *stream) { map visited; map >::iterator it; for (it = adj.begin(); it != adj.end(); it++) visited[it->first] = false; for (it = adj.begin(); it != adj.end(); it++) if (!visited[it->first]) { max_rank = max_ind = -1; dfs(it->first, visited); if (max_ind != -1) { nprinted++; print_block(stream, all_blocks[it->first]); } } } int comp_base(const void *a, const void *b) { return *(char *)a != *(char *)b; } int edit_distance(const void *s1, size_t l1, const void *s2, size_t l2, size_t nmemb, int (*comp)(const void*, const void*)) { unsigned int i; unsigned int j; size_t len = (l1 + 1) * (l2 + 1); char *p1; char *p2; unsigned int d1; unsigned int d2; unsigned int d3; unsigned int *d; unsigned int *dp; unsigned int res; if (l1 == 0) return l2; else if (l2 == 0) return l1; d = (unsigned int*)malloc(len * sizeof(unsigned int)); *d = 0; for(i = 1, dp = d + l2 + 1; i < l1 + 1; ++i, dp += l2 + 1) { *dp = (unsigned) i; } for(j = 1, dp = d + 1; j < l2 + 1; ++j, ++dp) { *dp = (unsigned) j; } for(i = 1, p1 = (char*) s1, dp = d + l2 + 2; i < l1 + 1; ++i, p1 += nmemb, ++dp) { for(j = 1, p2 = (char*) s2; j < l2 + 1; ++j, p2 += nmemb, ++dp) { if(!comp(p1, p2)) { *dp = *(dp - l2 - 2);// same base } else { d1 = *(dp - 1) + 1;// insertion d2 = *(dp - l2 - 1) + 1;// deletion d3 = *(dp - l2 - 2) + 1;// substitution *dp = MIN3(d1, d2, d3); } } } res = *(dp - 2); dp = NULL; free(d); return res; } int min_dist = 1; bool has_edge(int i, int j) { // lower path should be equal if (all_blocks[i].l[3] != all_blocks[j].l[3]) return false; if (abs((int)all_blocks[i].l[1].size() - (int)all_blocks[j].l[1].size()) > min_dist) return false; int ed = edit_distance(all_blocks[i].l[1].c_str(), all_blocks[i].l[1].size(), all_blocks[j].l[1].c_str(), all_blocks[j].l[1].size(), sizeof(char), comp_base); return (ed <= min_dist); } int main(int argc, char **argv) { ifstream bubbles_file; bubbles_file.open(argv[1]); if (argc < 3) { fprintf(stderr, "./cluster input.fasta output.fasta [min_dist]\n"); exit(0); } if (argc == 4) min_dist = atoi(argv[3]); string line; int nlines = 0; block cur; while (bubbles_file.good()) { getline(bubbles_file, line); nlines++; cur.l[(nlines-1) % 4] = line; if (nlines % 4 == 0) { // convert the sequences to a canonical form, lexicografically smaller (forward and reverse) if (comp_lower(reverse_complement(cur), cur)) cur = reverse_complement(cur); // extract the bcc number cur.bcc = extract_bcc(cur); cur.rank = extract_rank(cur); all_blocks.push_back(cur); } } FILE *output = fopen(argv[2], "w"); if (output == NULL) { fprintf(stderr, "Problem opening %s\n", argv[2]); exit(0); } // sort by BCC, all bubbles from the same BCC are adjacent sort(all_blocks.begin(), all_blocks.end(), comp_bcc); for (int i = 0; i < (int)all_blocks.size(); i++) { map >::iterator it; adj[i] = vector(); for (it = adj.begin(); it != adj.end(); it++) if (has_edge(i, it->first)) { adj[i].push_back(it->first); adj[it->first].push_back(i); } // finished the bubbles for a given BCC if ((i+1) == (int)all_blocks.size() || all_blocks[i].bcc != all_blocks[i+1].bcc) { print_clusters(output); map >().swap(adj); } } fclose(output); fprintf(stdout, "%d out of %d bubbles were outputed\n", nprinted, (int)all_blocks.size()); //fprintf(stderr, "nprinted = %d, total = %d, max_nodes = %d, max_edges = %d\n", nprinted, (int)all_blocks.size(), max_sz, max_edges); bubbles_file.close(); return 0; } kissplice-2.4.0-p1/modules/CMakeLists.txt000644 000765 000024 00000004372 12575016362 020345 0ustar00ishistaff000000 000000 INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ) ADD_EXECUTABLE(ks_clean_duplicates CleanDuplicates.cpp CycleCompression.cpp NGraph.cpp CGraph.cpp CEdge.cpp LabelledCEdge.cpp NNode.cpp NEdge.cpp Utils.cpp) install(TARGETS ks_clean_duplicates DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) ADD_EXECUTABLE(ks_bubble_enumeration BubbleEnumeration.cpp CycleCompression.cpp NGraph.cpp CGraph.cpp CEdge.cpp LabelledCEdge.cpp NNode.cpp NEdge.cpp Utils.cpp) install(TARGETS ks_bubble_enumeration DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) ADD_EXECUTABLE(ks_run_modules run.cpp CycleCompression.cpp NGraph.cpp CGraph.cpp CEdge.cpp LabelledCEdge.cpp NNode.cpp NEdge.cpp SplitBcc.cpp Utils.cpp) install(TARGETS ks_run_modules DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) ADD_EXECUTABLE(ks_error_removal ErrorRemoval.cpp CEdge.cpp LabelledCEdge.cpp Utils.cpp) install(TARGETS ks_error_removal DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) ADD_EXECUTABLE(ks_print_bcc PrintBcc.cpp Utils.cpp) install(TARGETS ks_print_bcc DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kissplice) # Declare use of c++11 features # TODO: This conditional should be removed as soon as cmake 2.8.12 or greater # becomes acceptable as a requirement since the solution using target_compile_options # (introduced in 2.8.12) is a lot cleaner. IF ((${CMAKE_VERSION} VERSION_LESS 2.8.12) ) SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c++11") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c++11") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") SET(CMAKE_C_FLAGS_DISTRIBUTION "${CMAKE_C_FLAGS_DISTRIBUTION} -std=c++11") SET(CMAKE_CXX_FLAGS_DISTRIBUTION "${CMAKE_CXX_FLAGS_DISTRIBUTION} -std=c++11") SET(CMAKE_C_FLAGS_PROFILING "${CMAKE_C_FLAGS_PROFILING} -std=c++11") SET(CMAKE_CXX_FLAGS_PROFILING "${CMAKE_CXX_FLAGS_PROFILING} -std=c++11") ELSE () target_compile_options(ks_clean_duplicates PRIVATE "-std=c++11") target_compile_options(ks_bubble_enumeration PRIVATE "-std=c++11") target_compile_options(ks_run_modules PRIVATE "-std=c++11") target_compile_options(ks_error_removal PRIVATE "-std=c++11") target_compile_options(ks_print_bcc PRIVATE "-std=c++11") ENDIF ()kissplice-2.4.0-p1/modules/CycleCompression.cpp000644 000765 000024 00000010662 12575016362 021571 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #include #include #include #include #include #include "debug.h" #include "NGraph.h" #include "Utils.h" #include "CycleCompression.h" #define MAX 1024 //MAX2 and MIN2 never used? TODO: Remove? #define MAX2(a,b) ((a) > (b) ? (a) : (b)) #define MIN2(a,b) ((a) < (b) ? (a) : (b)) using namespace std; idx_dir reverse_dir(idx_dir node) { return make_pair(node.first, reverse_dir(node.second)); } bool is_same_node( const idx_dir u, const idx_dir v) { return (v.first == u.first && v.second == u.second); } /*! * \brief Merge sequence information between two nodes and print it in the snp file * with bbc number, cycle and length of upper and lower path. * Returns the merged sequence * \param seq1 sequence of the upper path * \param seq2 sequence of the lower path * \param snp_log_file the file to be printing the two paths * \param bccid id of the bi-connected component being treated */ string merge_sequences( const string seq1, const string seq2 ) { string merged; for (int i = 0 ; i < (int)seq1.size(); i++) merged += (seq1[i] == seq2[i] ? seq1[i] : 'N'); return merged; } void output_sequences( string seq1, string seq2, FILE * snp_log_file, const int bccid, const int cycleNum, const int contextL, const int contextR, const int kValue) { int u_len = (int)seq1.size(); int l_len = (int)seq2.size(); if( contextL != 0 || contextR !=0 ) { seq1 = toLowerContext(seq1, contextL, contextR); seq2 = toLowerContext(seq2, contextL, contextR); } if (u_len- (contextL + contextR) > 2* kValue +1) // multiple SNP { fprintf(snp_log_file, ">bcc_%d|Cycle_%d|Type_0b|upper_path_Length_%d\n", bccid, cycleNum, u_len- (contextL + contextR)); fprintf(snp_log_file, "%s\n", seq1.c_str()); fprintf(snp_log_file, ">bcc_%d|Cycle_%d|Type_0b|lower_path_Length_%d\n", bccid, cycleNum, l_len- (contextL + contextR)); fprintf(snp_log_file, "%s\n", seq2.c_str()); } else // single SNP { fprintf(snp_log_file, ">bcc_%d|Cycle_%d|Type_0a|upper_path_Length_%d\n", bccid, cycleNum, u_len- (contextL + contextR)); fprintf(snp_log_file, "%s\n", seq1.c_str()); fprintf(snp_log_file, ">bcc_%d|Cycle_%d|Type_0a|lower_path_Length_%d\n", bccid, cycleNum, l_len- (contextL + contextR)); fprintf(snp_log_file, "%s\n", seq2.c_str()); } } kissplice-2.4.0-p1/modules/CycleCompression.h000644 000765 000024 00000005232 12575016362 021233 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #include #include #include #include "NGraph.h" #include "Utils.h" #ifndef CYCLE_COMPRESSION_H #define CYCLE_COMPRESSION_H // Auxiliary functions string merge_sequences( const string seq1, const string seq2); void output_sequences( string seq1, string seq2, FILE * snp_log_file, const int bccid, const int cycleNumconst, int contextL, const int contextR, const int kValue); bool is_same_node(const idx_dir u, const idx_dir v); idx_dir reverse_dir(idx_dir node); #endif /* CYCLE_COMPRESSION_H */ kissplice-2.4.0-p1/modules/debug.h000644 000765 000024 00000004423 12575016362 017041 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Alice Julien-Laferriere * David Parsons * Vincent Miele * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #ifndef DEBUG_H #define DEBUG_H #ifdef DEBUG_MODE #define DEBUG(a) (a) #else /* DEBUG */ #define DEBUG(a) #endif #endif /* DEBUG_H */ kissplice-2.4.0-p1/modules/EntropyFilter.cpp000644 000765 000024 00000010016 12575016362 021107 0ustar00ishistaff000000 000000 #include #include #include #include #include #include #include #include using namespace std; #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MIN3(a,b,c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) struct block { string l[4], bcc; double rank; }; static char complement(char b) { switch(b) { case 'A': return 'T'; case 'T': return 'A'; case 'G': return 'C'; case 'C': return 'G'; case 'a': return 't'; case 't': return 'a'; case 'g': return 'c'; case 'c': return 'g'; case 'N': return 'N'; case '*': return '*'; } return '?'; } string reverse_complement(string seq) { string s(seq.begin(),seq.end()); string::iterator pos; for (pos = s.begin(); pos != s.end(); ++pos) { // cout << *pos; } // cout << endl; reverse(s.begin(), s.end()); for(pos=s.begin();pos!=s.end();++pos) *pos=complement(*pos); return s; } block reverse_complement(block &b) { block res; res.l[1] = reverse_complement(b.l[1]); res.l[3] = reverse_complement(b.l[3]); res.l[0] = b.l[0]; res.l[2] = b.l[2]; return res; } void print_block(FILE *stream, block &b) { for (int i = 0; i < 4; i++) fprintf(stream, "%s\n", b.l[i].c_str()); } int nucToNumber(const char& nuc) { switch (nuc) { case 'A': return 0; case 'C': return 1; case 'G': return 2; case 'T': return 3; default: return 4; } } static double MinusPlogP(double value, double base) { return -1 * value * log(value)/log(base); } bool valid_nuc(char nuc) { return nuc != 'N' && nuc != '*' && nuc != '?'; } double entropy3(const string& window_nucs) { size_t window_length = window_nucs.length(); vector kmer_counts(444, 0); float subseqs = 0; for (size_t i = 0; i < window_length - 3; ++i) { char nuc1 = window_nucs.at(i); char nuc2 = window_nucs.at(i+1); char nuc3 = window_nucs.at(i+2); if (valid_nuc(nuc1) && valid_nuc(nuc2) && valid_nuc(nuc3)) { kmer_counts[nucToNumber(nuc1) + nucToNumber(nuc2)*10 + nucToNumber(nuc3)*10] += 1; subseqs+= 1; } } float entropy = 0, base = 64; if (window_length < 66) base = (float)window_length; for (size_t i = 0; i < kmer_counts.size(); ++i) { int count = kmer_counts.at(i); if (count !=0) { float p = static_cast(count)/subseqs; entropy += MinusPlogP(p, base); } } return entropy; } bool comp_bcc(block a, block b) { return a.bcc < b.bcc; } string extract_bcc(block &b) { int sep = b.l[0].find_first_of("|"); return b.l[0].substr(0, sep); } int main(int argc, char **argv) { ifstream bubbles_file; bubbles_file.open(argv[1]); double threshold = 0.70; if (argc < 3) { fprintf(stderr, "./entropy_filter input.fasta output.fasta [threshold]\n"); exit(0); } if (argc == 4) threshold = atof(argv[3]); fprintf(stderr, "%lf\n", threshold); string line; int nlines = 0; block cur; map bcc_count; vector all_blocks; FILE *output = fopen(argv[2], "w"), *filtered = fopen("removed.fa", "w"); if (output == NULL) { fprintf(stderr, "Problem opening %s\n", argv[2]); exit(0); } int nprinted = 0; while (bubbles_file.good()) { getline(bubbles_file, line); nlines++; cur.l[(nlines-1) % 4] = line; if (nlines % 4 == 0) { cur.bcc = extract_bcc(cur); all_blocks.push_back(cur); if (bcc_count.find(cur.bcc) == bcc_count.end()) bcc_count[cur.bcc] = 0; bcc_count[cur.bcc] = bcc_count[cur.bcc] + 1; } } for (int i = 0; i < (int)all_blocks.size(); i++) if (entropy3(all_blocks[i].l[1]) > threshold || bcc_count[all_blocks[i].bcc] <= 10) { nprinted++; print_block(output, all_blocks[i]); } else { print_block(filtered, all_blocks[i]); } fclose(output); fclose(filtered); bubbles_file.close(); fprintf(stdout, "%d out of %d bubbles were outputed\n", nprinted, (int)all_blocks.size()); return 0; } kissplice-2.4.0-p1/modules/ErrorRemoval.cpp000644 000765 000024 00000017646 12575016362 020740 0ustar00ishistaff000000 000000 #include #include #include #include #include "Utils.h" #include "LabelledCEdge.h" #define MAX 1024 using namespace std; struct NodeSeq { char *seq; int node; NodeSeq(char *initSeq, int initNode) : seq(initSeq), node(initNode) { } bool operator== (const NodeSeq& rhs) const { return (strcmp(seq, rhs.seq) == 0); } bool operator< (const NodeSeq& rhs) const { return (strcmp(seq, rhs.seq) < 0); } }; int count_nb_lines( FILE* file ) { int ch, number_of_lines = 0; while (EOF != (ch=getc(file))) if ('\n' == ch) number_of_lines++; // Set the cursor back to the begining of the file. rewind(file); // Don't care if the last line has a '\n' or not. We over-estimate it. return number_of_lines + 1; } void readNodeFile(char* nodes_fname, vector& nodesF, vector& nodesR, int k_val ) { FILE* node_file = open_file(nodes_fname); char* buffer = new char[100 * MAX]; char* seq; nodesF.reserve(count_nb_lines(node_file)); nodesR.reserve(count_nb_lines(node_file)); while ( fgets(buffer, 100 * MAX, node_file) != NULL ) { char* p; if (strlen(buffer) == 100 * MAX) { p = strtok(buffer, "\t\n"); fprintf(stdout, "ERROR: node %s with sequence larger than %d!", p, 100 * MAX); exit(0); } // Node label, should be contiguous p = strtok( buffer, "\t\n" ); int node = atoi(p); // Node seq p = strtok( NULL, "\t\n" ); // We need the prefix of size k seq = new char[k_val + 1]; strncpy( seq, p, k_val); seq[k_val] = '\0'; nodesF.push_back( NodeSeq(seq, node) ); // And the prefix of size k in the reverse complement seq = new char[k_val + 1]; string rev_comp = reverse_complement(string(p)); strncpy( seq, rev_comp.c_str(), k_val); seq[k_val] = '\0'; nodesR.push_back( NodeSeq(seq, node) ); //printf("%d %s %s\n", node, p, rev_comp.c_str()); } sort(nodesF.begin(), nodesF.end()); sort(nodesR.begin(), nodesR.end()); delete [] buffer; fclose(node_file); } void readEdgeFile( char* nodes_fname, vector& edges ) { FILE *edge_file = open_file(nodes_fname); char* buffer = new char[100 * MAX]; char* u = new char[MAX]; char* v = new char[MAX]; char* label = new char[MAX]; edges.reserve(count_nb_lines(edge_file)); while ( fgets(buffer, 100 * MAX, edge_file) != NULL ) { char* p; // outgoing node p = strtok( buffer, "\t\n" ); strcpy( u, p ); // incoming node p = strtok( NULL, "\t\n" ); strcpy( v, p ); // edge label p = strtok( NULL, "\t\n" ); strcpy(label, p); edges.push_back( LabelledCEdge( atoi(u), atoi(v), label ) ); } sort( edges.begin(), edges.end() ); delete [] buffer; delete [] u; delete [] v; delete [] label; fclose(edge_file); } // rev_seq needs to be global! let's make sure we allocate only once char *rev_seq = NULL; char *revComp(char *seq) { int size = strlen(seq); if (rev_seq == NULL) rev_seq = new char[size + 1]; for (int i = size - 1; i >= 0; i--) rev_seq[(size - 1) - i] = complement(seq[i]); rev_seq[size] = '\0'; return rev_seq; } int findNode(char *query, vector& nodes) { vector::iterator low, low_r; char *query_r = revComp(query); // The value of "x" in NodeSeq(query, x) doesn't matter low = lower_bound(nodes.begin(), nodes.end(), NodeSeq(query, 0)); low_r = lower_bound(nodes.begin(), nodes.end(), NodeSeq(query_r, 0)); if (low != nodes.end() && *low == NodeSeq(query, 0)) return low->node; if (low_r != nodes.end() && *low_r == NodeSeq(query_r, 0)) return low_r->node; return -1; } void readCounts(char* counts_fname, vector& nodesF, vector& nodesR, vector& countsF, vector& countsR) { FILE *count_file = open_file(counts_fname); char* buffer = new char[100 * MAX]; while ( fgets(buffer, 100 * MAX, count_file) != NULL ) { char* p; // Sequence p = strtok( buffer, "\t\n " ); int nodeF = findNode(p, nodesF); int nodeR = findNode(p, nodesR); // Count p = strtok( NULL, "\t\n " ); if (nodeF != -1) countsF[nodeF] += atoi(p); if (nodeR != -1) countsR[nodeR] += atoi(p); } delete [] buffer; fclose(count_file); } int findEdge(vector& allEdges, LabelledCEdge e) { vector::iterator low; low = lower_bound(allEdges.begin(), allEdges.end(), e); if (low != allEdges.end()) return low - allEdges.begin(); fprintf(stderr, "inconsistent graph!"); return -1; } // Move this to CEdge LabelledCEdge reverse(LabelledCEdge e) { char *label = new char[3]; label[0] = e.label[1] == 'F' ? 'R' : 'F'; label[1] = e.label[0] == 'F' ? 'R' : 'F'; label[2] = '\0'; return LabelledCEdge(e.getSecond(), e.getFirst(), label); } void errorRemoval(vector& allEdges, int nbNodes, double cutoff, vector& removed, vector& countsF, vector& countsR) { int offset = 0; for ( int src = 0 ; src < nbNodes; src++ ) { // Count the number of edges (they are ordered in all_edges) of node src int size = 0; while ( offset + size < (int) allEdges.size() && allEdges[offset + size].getFirst() == src ) size++; // Compute the sum of coverage for each direction int sum_F = 0, sum_R = 0; for (int k = 0; k < size; k++) if (!removed[offset + k]) { int target = allEdges[offset + k].getSecond(); if (allEdges[offset + k].label[0] == 'F') sum_F += (allEdges[offset + k].label[1] == 'F') ? countsF[target] : countsR[target]; if (allEdges[offset + k].label[0] == 'R') sum_R += (allEdges[offset + k].label[1] == 'F') ? countsF[target] : countsR[target]; } // Remove the edges with relative coverage below cutoff for (int k = 0; k < size; k++) if (!removed[offset + k]) { int target = allEdges[offset + k].getSecond(); int count = (allEdges[offset + k].label[1] == 'F') ? countsF[target] : countsR[target]; double ratio; ratio = (double)count / (double)sum_F; if (allEdges[offset + k].label[0] == 'F' && ratio < cutoff) { removed[offset + k] = true; removed[findEdge(allEdges, reverse(allEdges[offset+k]))] = true; } ratio = (double)count / (double)sum_R; if (allEdges[offset + k].label[0] == 'R' && ratio < cutoff) { removed[offset + k] = true; removed[findEdge(allEdges, reverse(allEdges[offset+k]))] = true; } } offset += size; } } int main( int argc, char** argv ) { string base_name = "graph"; if ( argc < 6 ) { fprintf( stderr, "Wrong number of arguments!\n" ); fprintf( stderr, "Usage: ./error_removal edge_file node_file k_value k_mer_count_file cutoff [base_name]\n" ); return 0; } if ( argc == 7 ) { base_name = argv[6]; } int k_value = atoi( argv[3] ); double cutoff = atof( argv[5] ); // Read edge file vector allEdges; readEdgeFile( argv[1], allEdges ); // Read node file vector nodesF, nodesR; readNodeFile(argv[2], nodesF, nodesR, k_value ); // Read count file vector countsF(nodesF.size(), 0), countsR(nodesR.size(), 0); readCounts(argv[4], nodesF, nodesR, countsF, countsR); vector removed(allEdges.size(), false); errorRemoval(allEdges, (int)nodesF.size(), cutoff, removed, countsF, countsR); int nb_removed = 0; FILE *output = fopen((base_name + ".edges").c_str(), "w"); FILE *removed_out = fopen((base_name + "_removed.edges").c_str(), "w"); for (int i = 0 ; i < (int)allEdges.size(); i++) if (!removed[i]) fprintf(output, "%d\t%d\t%s\n", allEdges[i].getFirst(), allEdges[i].getSecond(), allEdges[i].label); else { fprintf(removed_out, "%d\t%d\t%s\n", allEdges[i].getFirst(), allEdges[i].getSecond(), allEdges[i].label); nb_removed++; } fclose(output); fclose(removed_out); fprintf(stdout, "%d out of %d edges removed\n", nb_removed, (int)allEdges.size()); return 0; } kissplice-2.4.0-p1/modules/LabelledCEdge.cpp000644 000765 000024 00000012765 12575016362 020712 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ // =========================================================================== // Include Libraries // =========================================================================== #include // =========================================================================== // Include Project Files // =========================================================================== #include "LabelledCEdge.h" // =========================================================================== // Declare Used Namespaces // =========================================================================== //############################################################################ // # // Class LabelledCEdge # // # //############################################################################ // =========================================================================== // Static attributes // =========================================================================== // =========================================================================== // Constructors // =========================================================================== LabelledCEdge::LabelledCEdge( const LabelledCEdge& model ) : CEdge( model ) { strcpy( label, model.label ); } /*! * */ LabelledCEdge::LabelledCEdge( int f, int s, char* _label ) : CEdge( f, s ) { strcpy( label, _label ); }; LabelledCEdge::LabelledCEdge( CEdge& edge, char* _label ) : CEdge( edge ) { strcpy( label, _label ); }; // =========================================================================== // Destructor // =========================================================================== // =========================================================================== // Operators // =========================================================================== bool LabelledCEdge::operator<( const LabelledCEdge& that ) const { if ( static_cast( *this ) == static_cast( that ) ) { if ( label[0] == that.label[0] ) { return label[1] < that.label[1]; } return label[0] < that.label[0]; } return static_cast( *this ) < static_cast( that ); } bool LabelledCEdge::operator==( const LabelledCEdge& that ) const { return static_cast( *this ) == static_cast( that ) && label[0] == that.label[0] && label[1] == that.label[1]; } // =========================================================================== // Public Methods // =========================================================================== // =========================================================================== // Protected Methods // =========================================================================== // =========================================================================== // Non inline accessors // =========================================================================== kissplice-2.4.0-p1/modules/LabelledCEdge.h000644 000765 000024 00000015762 12575016362 020357 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #ifndef LABELLED_EDGE_H #define LABELLED_EDGE_H // =========================================================================== // Include Libraries // =========================================================================== #include #include // =========================================================================== // Include Project Files // =========================================================================== #include "CEdge.h" // =========================================================================== // Class declarations // =========================================================================== // =========================================================================== // Declare Used Namespaces // =========================================================================== //! Labelled edge /*! * \brief Class meant to be used by NGraph to represent edges. * Inherits from CEdge class. Contains: * a pair of int (u -> v edge), each int (u or v) being a node. * a label representing, most of the time, the direction of the edge (FR, RR, RF, FF) */ class LabelledCEdge : public CEdge { public : // ======================================================================= // Enums // ======================================================================= // ======================================================================= // Constructors // ======================================================================= LabelledCEdge( const LabelledCEdge& model ); LabelledCEdge( int f, int s, char* _label ); LabelledCEdge( CEdge& edge, char* _label ); // ======================================================================= // Destructor // ======================================================================= // ======================================================================= // Accessors: getters // ======================================================================= // ======================================================================= // Accessors: setters // ======================================================================= // ======================================================================= // Operators // ======================================================================= bool operator<( const LabelledCEdge& that ) const; bool operator==( const LabelledCEdge& that ) const; // ======================================================================= // Public Methods // ======================================================================= // ======================================================================= // Public Attributes // ======================================================================= char label[3]; protected : // ======================================================================= // Forbidden Constructors // ======================================================================= LabelledCEdge( void ) { printf( "%s:%d: error: call to forbidden constructor.\n", __FILE__, __LINE__ ); exit( EXIT_FAILURE ); }; // ======================================================================= // Protected Methods // ======================================================================= // ======================================================================= // Protected Attributes // ======================================================================= }; // =========================================================================== // Getters' definitions // =========================================================================== // =========================================================================== // Setters' definitions // =========================================================================== // =========================================================================== // Inline Operators' definitions // =========================================================================== // =========================================================================== // Inline functions' definition // =========================================================================== #endif // LABELLED_EDGE_H kissplice-2.4.0-p1/modules/memused000755 000765 000024 00000000444 12575016362 017166 0ustar00ishistaff000000 000000 #!/bin/bash "$@" & cd /proc/$! max=0 while [ -f status ] do sleep 0.1 if [ -f status ] then mem=`cat status | grep VmHWM | tr -s [:blank:] | cut -d ' ' -f 2` if [ "$mem" -gt "$max" ] then max=$mem fi fi; done echo "maximal memory used ( kilobyte(s) (K / Kb))" $max kissplice-2.4.0-p1/modules/NEdge.cpp000644 000765 000024 00000012271 12575016362 017270 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ // =========================================================================== // Include Libraries // =========================================================================== // =========================================================================== // Include Project Files // =========================================================================== #include "NEdge.h" #include "NGraph.h" #include "Utils.h" // =========================================================================== // Declare Used Namespaces // =========================================================================== //############################################################################ // # // Class NEdge # // # //############################################################################ // =========================================================================== // Static attributes // =========================================================================== // =========================================================================== // Constructors // =========================================================================== NEdge::NEdge( const NEdge & model ) { node = model.node; labels = model.labels; } NEdge::NEdge( int _node, string _labels ) { node = _node; labels = _labels; } NEdge::NEdge( int _node, char label1, char label2 ) { node = _node; char buf[3] = { label1, label2, '\0' }; labels = string( buf ); } // =========================================================================== // Destructors // =========================================================================== NEdge::~NEdge( void ) { } // =========================================================================== // Operators // =========================================================================== // =========================================================================== // Public Methods // =========================================================================== void NEdge::add_label( string label ) { labels += label; }; void NEdge::del_all_but_first( void ) { labels.erase( 2 ); }; void NEdge::revert_dir( int i ) { labels[i] = reverse_dir(labels[i]); }; // =========================================================================== // Protected Methods // =========================================================================== // =========================================================================== // Non inline accessors // =========================================================================== kissplice-2.4.0-p1/modules/NEdge.h000644 000765 000024 00000016245 12575016362 016742 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ #ifndef NEDGE_H #define NEDGE_H // =========================================================================== // Include Libraries // =========================================================================== #include #include #include // =========================================================================== // Include Project Files // =========================================================================== // =========================================================================== // Declare Used Namespaces // =========================================================================== using namespace std; /* The node class is used into the adjacency list of the NGraph object. It keeps the whole information of the link between a node u and its outgoing neighbours: the index of the node v and the label of the arc linking u to v. */ class NEdge { public : // ======================================================================= // Enums // ======================================================================= // ======================================================================= // Constructors // ======================================================================= NEdge( const NEdge& model ); NEdge( int node, string labels ); NEdge( int node, char label1, char label2 ); // ======================================================================= // Destructors // ======================================================================= ~NEdge( void ); // ======================================================================= // Accessors: getters // ======================================================================= inline int get_node( void ) const; inline string get_labels( void ) const; // ======================================================================= // Accessors: setters // ======================================================================= inline void set_node( int node ); inline void set_labels( string labels ); // ======================================================================= // Operators // ======================================================================= // ======================================================================= // Public Methods // ======================================================================= void add_label( string label ); void del_all_but_first( void ); void revert_dir( int i ); // ======================================================================= // Public Attributes // ======================================================================= protected : // ======================================================================= // Forbidden Constructors // ======================================================================= NEdge( void ) { printf( "%s:%d: error: call to forbidden constructor.\n", __FILE__, __LINE__ ); exit( EXIT_FAILURE ); }; // ======================================================================= // Protected Methods // ======================================================================= // ======================================================================= // Protected Attributes // ======================================================================= //! Index of the node int node; //! Label of the incoming edge string labels; }; // =========================================================================== // Getters' definitions // =========================================================================== int NEdge::get_node( void ) const { return node; }; string NEdge::get_labels( void ) const { return labels; }; // =========================================================================== // Setters' definitions // =========================================================================== void NEdge::set_node( int _node ) { node = _node; }; void NEdge::set_labels( string _labels ) { labels = _labels; }; // =========================================================================== // Inline Operators' definitions // =========================================================================== // =========================================================================== // Inline functions' definition // =========================================================================== #endif // NEDGE_H kissplice-2.4.0-p1/modules/NGraph.cpp000644 000765 000024 00000106320 12575016362 017464 0ustar00ishistaff000000 000000 /* *************************************************************************** * * KisSplice * de-novo calling alternative splicing events from RNA-seq data. * * *************************************************************************** * * Copyright INRIA * contributors : Vincent Lacroix * Pierre Peterlongo * Gustavo Sacomoto * Vincent Miele * Alice Julien-Laferriere * David Parsons * * pierre.peterlongo@inria.fr * vincent.lacroix@univ-lyon1.fr * * This software is a computer program whose purpose is to detect alternative * splicing events from RNA-seq data. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ // =========================================================================== // Include Libraries // =========================================================================== #include #include #include #include #include #include #include #include #include #include #include // =========================================================================== // Include Project Files // =========================================================================== #include #include #include #include // =========================================================================== // Declare Used Namespaces // =========================================================================== using namespace std; // =========================================================================== // Constant // =========================================================================== #define MAX 1024 //############################################################################ // # // Class NGraph # // # //############################################################################ // =========================================================================== // Constructors // =========================================================================== NGraph::NGraph( int kValue, int outputtedSnps ) { _kValue = kValue; _nbOutput = outputtedSnps; } NGraph::NGraph( const NGraph &model ) { _kValue = model._kValue; _nodes = model._nodes; _nbOutput = model._nbOutput; _nodeToStr = model._nodeToStr; _strToNode = model._strToNode; } /*! TODO : check : Create a NGraph for the BCC passed in edges ? all_edges ? */ NGraph::NGraph( CGraph& cgraph, vector& seqs, vector& all_edges, vector& edges ) { _nbOutput = 0; for ( int i = 0 ; i < (int)edges.size() ; i++ ) { int u = edges[i].getFirst(); int v = edges[i].getSecond(); //~ printf( "contructing %s %s (0x%x, 0x%x)\n", seqs[u], seqs[v], seqs[u], seqs[v] ); //~ getchar(); insert_node( to_str(u), to_str( seqs[u] ) ); insert_node( to_str(v), to_str( seqs[v] ) ); insert_bidirected_edges( all_edges, edges[i] ); edges[i].swap_ends(); insert_bidirected_edges( all_edges, edges[i] ); } _kValue = cgraph.k_value; expand_parallel_edges(); } // =========================================================================== // Destructors // =========================================================================== NGraph::~NGraph(void) { } // =========================================================================== // Public Methods // =========================================================================== void NGraph::insert_empty_node( string u ) { _strToNode[u] = _nodeToStr.size(); // _nodeToStr.size() corresponds to the current number of nodes in the graph _nodeToStr.push_back(u); _nodes.push_back( NNode() ); } // Insert the new edge u->v (label). // If the edge u->v is already present with a different label, concatenate